This lecture is inspired in its structure and organisation by the “Introduction to data analysis with R and Bioconductor” - https://carpentries-incubator.github.io/bioc-intro/

1 Step 0: R and RStudio, know your tools

1.1 What is R? Why should I use R?

Available at www.r-project.org

  • free statistical environment for interactive use
  • intepreted, functional scripting/programming language - you type in, you see output
  • descends from the S language, written by statisticians for statisticians

What can you do with R?

  • Anything!
    • do calculations
    • write functions
    • analyse data. ALL the data. Well, almost. But really, almost.
    • apply advanced statistical techniques
    • do beautiful & publication-ready plots
    • develop interactive web-applications
    • presentations & documents (this one)

Why should I use R?

  • it works! And it is quite powerful
  • it is free, open-source, and available for all OS
  • you can really do whatever you might aim to do in terms of statistics
  • it offers awesome possibilities for (interactive) graphing
  • it has a wide, active and competent community (ok, communities: statistics, bioinformatics, machine learning, …)
  • it can be extended with packages. More power!
  • escape Point-and-Click-land, you work with syntax: you can use, re-use elements, validate & reproduce analysis

Why should I not use R?

  • you use it or lose it
  • the learning curve might be steep
  • can be frustrating if you have errors
  • help might be available, but it is very technical
  • many packages, a blessing and a curse: how many, how good

1.2 Let’s get started!

Get R - and RStudio

Alternatives:

  • OpenAnalytics Architect (https://www.getarchitect.io/)
  • Microsoft R Tools for Visual Studio (www.visualstudio.com/vs/rtvs/)
  • Emacs Speaks Statistics…

1.3 First ride: look around you

Open up RStudio - You’ll have four panes

  • the Source for your scripts and documents (top-left, in the default layout)
  • your Environment/History (top-right),
  • your Files/Plots/Packages/Help/Viewer (bottom-right), and
  • the R Console (bottom-left).

Want to customize this?

Tools -> Global Options -> Pane Layout

Advantages of an IDE

  • all in one window!
  • keyboard shortcuts, autocompletion, highlighting -> type easier, do less errors

1.4 Folder structure

It is good practice to keep a set of related data, analyses, and text self-contained in a single folder, called the working directory.

Why?

  • Easier to have “self-contained” research units!
  • A project “does not interfere” with other projects
  • Gives a structure, easier to find things, use, reuse
  • Someone else (including future you) can understand what goes on

How?

RStudio projects!
Custom settings, per project.

Let’s create one live now - and have the workspace NOT saved

What is the best structure?

One, used consistently - not gonna touch on naming things as it can get hot quickly :)

With R…

dir.create()

file.edit()

Where am I doing things?

The working directory is the place from where R will be looking for and saving the files.

getwd()/setwd(), but not in your scripts (fails on others’ computers!)

1.5 Interacting with R

Instructions, commands.

Scripts, console - use the editor and have a complete record on what you did!

Shortcuts FTW!

Even better: Reproducible documents, with R Markdown

Nice resources on top: * RStudio cheatsheet about the RStudio IDE! * the internet/rstats community!

1.6 Seeking help

  • ?
  • ??
  • built-in RStudio help interface - and shortcuts!

1.6.1 Where to ask for help?

  • your neighbour - covid-conform, do interact within each other!
  • your colleagues
  • rdocumentation.org website
  • the web: google, StackOverflow

The main point: describe well your problem, “help others help you”

Others need to reproduce your error to help you better: saveRDS(), dput(), sessionInfo()

1.7 R packages

R packages…

  • are fundamental components of R ecosystem
  • extend base R functionality for a specific purpose
  • bundle new functions, data sets, and documentation
  • are contributed by independent developers
  • have dependency management

Repositories:

  • CRAN: Managed official package repository network
  • Bioconductor: curated bioinformatics packages (vignettes mandatory, integrated ecosystem!)
  • GitHub: un-managed, bleeding edge - but also excellent ones (“just not on CRAN”)

My contributions so far:

  • flowcatchR https://bioconductor.org/packages/flowcatchR/
  • pcaExplorer https://bioconductor.org/packages/pcaExplorer/
  • ideal https://bioconductor.org/packages/ideal/
  • iSEE (https://bioconductor.org/packages/iSEE)
  • GeneTonic (https://bioconductor.org/packages/GeneTonic)

1.7.1 How to use packages

  • Install: once (for every major R version)
  • Load: in each session
  • Use like any base R functionality

For Bioconductor packages…

install.packages("BiocManager")
library("BiocManager")
BiocManager::install()

Relevant commands:

  • install.packages("packagename") - check it online at CRAN!
  • installed.packages()
  • .libPaths()
  • update.packages()
  • library("packagename")
  • help(package="packagename"), data(), browseVignettes(), vignette(), citation("packagename")

Something you might have already done:

BiocManager::install("SummarizedExperiment")
BiocManager::install("DESeq2")

2 Step 1: Introduction to R

Here we will touch on the first commands in R, so that you can

  • Define the following terms as they relate to R: object, assign, call, function, arguments, options.
  • Assign values to objects in R.
  • Learn how to name objects
  • Use comments to inform script.
  • Solve simple arithmetic operations in R.
  • Call functions and use arguments to change their default options.
  • Inspect the content of vectors and manipulate their content.
  • Subset and extract values from vectors.
  • Analyze vectors with missing data.

2.1 R is a powerful calculator

… but not just that.

Type the following

2 + 2
# [1] 4
log(2)
# [1] 0.6931472
347 * 73841
# [1] 25622827
7/2
# [1] 3.5
7%/%2
# [1] 3
7%%2
# [1] 1

2.2 🎶 Help! 🎶

# this calls the help for a function to plot a histogram
?hist
# this is just the same
help(hist) ## what about ??
?apropos
apropos("row")
#   [1] ".row"                                ".rowMeans"                          
#   [3] ".rowNamesDF<-"                       ".rowSums"                           
#   [5] ".rs.api.tutorialLaunchBrowser"       ".rs.explorer.defaultRowLimit"       
#   [7] ".rs.formatRowNames"                  ".rs.isBrowserActive"                
#   [9] ".rs.nrow"                            ".rs.refreshShinyLaunchBrowserOption"
#  [11] ".rs.tutorial.launchBrowser"          "add_row"                            
#  [13] "add_row"                             "add_rownames"                       
#  [15] "arrow"                               "arrows"                             
#  [17] "as_tibble_row"                       "auto_browse"                        
#  [19] "bind_rows"                           "bindROWS"                           
#  [21] "bindROWS"                            "bindROWS"                           
#  [23] "browseEnv"                           "browseKEGG"                         
#  [25] "browser"                             "browserCondition"                   
#  [27] "browserSetDebug"                     "browserText"                        
#  [29] "browseUCSCtrack"                     "browseURL"                          
#  [31] "browseVignettes"                     "colAvgsPerRowSet"                   
#  [33] "colAvgsPerRowSet"                    "column_to_rownames"                 
#  [35] "combineRows"                         "cur_group_rows"                     
#  [37] "db_query_rows"                       "dplyr_row_slice"                    
#  [39] "elementNROWS"                        "elementNROWS"                       
#  [41] "extractROWS"                         "extractROWS"                        
#  [43] "group_rows"                          "has_rownames"                       
#  [45] "indexByRow"                          "makeClassinfoRowForCompactPrinting" 
#  [47] "mergeROWS"                           "n2mfrow"                            
#  [49] "narrow"                              "narrow"                             
#  [51] "narrow"                              "new_rowwise_df"                     
#  [53] "nrow"                                "nrow"                               
#  [55] "nrow"                                "nrow"                               
#  [57] "nrow"                                "nrow"                               
#  [59] "nrow"                                "NROW"                               
#  [61] "NROW"                                "NROW"                               
#  [63] "nrows"                               "nrows"                              
#  [65] "panel_rows"                          "PlantGrowth"                        
#  [67] "remove_rownames"                     "replaceROWS"                        
#  [69] "replaceROWS"                         "row"                                
#  [71] "row_number"                          "row.names"                          
#  [73] "row.names.data.frame"                "row.names.default"                  
#  [75] "row.names<-"                         "row.names<-.data.frame"             
#  [77] "row.names<-.default"                 "rowAlls"                            
#  [79] "rowAlls"                             "rowAnyMissings"                     
#  [81] "rowAnyNAs"                           "rowAnyNAs"                          
#  [83] "rowAnys"                             "rowAnys"                            
#  [85] "rowAvgsPerColSet"                    "rowAvgsPerColSet"                   
#  [87] "rowCollapse"                         "rowCollapse"                        
#  [89] "rowCounts"                           "rowCounts"                          
#  [91] "rowCummaxs"                          "rowCummaxs"                         
#  [93] "rowCummins"                          "rowCummins"                         
#  [95] "rowCumprods"                         "rowCumprods"                        
#  [97] "rowCumsums"                          "rowCumsums"                         
#  [99] "rowData"                             "rowData"                            
# [101] "rowData<-"                           "rowDataColorMap"                    
# [103] "rowDataColorMap<-"                   "RowDataPlot"                        
# [105] "RowDataTable"                        "rowDiffs"                           
# [107] "rowDiffs"                            "rowid_to_column"                    
# [109] "rowIQRDiffs"                         "rowIQRDiffs"                        
# [111] "rowIQRs"                             "rowIQRs"                            
# [113] "rowLogSumExps"                       "rowLogSumExps"                      
# [115] "rowMadDiffs"                         "rowMadDiffs"                        
# [117] "rowMads"                             "rowMads"                            
# [119] "rowMax"                              "rowMaxs"                            
# [121] "rowMaxs"                             "rowMeans"                           
# [123] "rowMeans"                            "rowMeans2"                          
# [125] "rowMeans2"                           "rowMedians"                         
# [127] "rowMedians"                          "rowMedians"                         
# [129] "rowMin"                              "rowMins"                            
# [131] "rowMins"                             "rownames"                           
# [133] "rownames"                            "rownames"                           
# [135] "rownames"                            "rownames"                           
# [137] "ROWNAMES"                            "ROWNAMES"                           
# [139] "rownames_to_column"                  "rownames<-"                         
# [141] "rownames<-"                          "rownames<-"                         
# [143] "rownames<-"                          "ROWNAMES<-"                         
# [145] "ROWNAMES<-"                          "rowOrderStats"                      
# [147] "rowOrderStats"                       "rowPair"                            
# [149] "rowPair<-"                           "rowPairNames"                       
# [151] "rowPairNames<-"                      "rowPairs"                           
# [153] "rowPairs<-"                          "rowProds"                           
# [155] "rowProds"                            "rowQ"                               
# [157] "rowQuantiles"                        "rowQuantiles"                       
# [159] "rowRanges"                           "rowRanges"                          
# [161] "rowRanges"                           "rowRanges<-"                        
# [163] "rowRanks"                            "rowRanks"                           
# [165] "rows_append"                         "rows_delete"                        
# [167] "rows_insert"                         "rows_patch"                         
# [169] "rows_update"                         "rows_upsert"                        
# [171] "rowSdDiffs"                          "rowSdDiffs"                         
# [173] "rowSds"                              "rowSds"                             
# [175] "rowSelectionColorMap"                "rowSubset"                          
# [177] "rowSubset<-"                         "rowsum"                             
# [179] "rowsum.data.frame"                   "rowsum.default"                     
# [181] "rowsum.DGEList"                      "rowsum.SummarizedExperiment"        
# [183] "rowSums"                             "rowSums"                            
# [185] "rowSums2"                            "rowSums2"                           
# [187] "rowTabulates"                        "rowTabulates"                       
# [189] "rowVarDiffs"                         "rowVarDiffs"                        
# [191] "rowVars"                             "rowVars"                            
# [193] "rowWeightedMads"                     "rowWeightedMads"                    
# [195] "rowWeightedMeans"                    "rowWeightedMeans"                   
# [197] "rowWeightedMedians"                  "rowWeightedMedians"                 
# [199] "rowWeightedSds"                      "rowWeightedSds"                     
# [201] "rowWeightedVars"                     "rowWeightedVars"                    
# [203] "rowwise"                             "sameAsPreviousROW"                  
# [205] "separate_rows"                       "separate_rows_"                     
# [207] "tibble_row"                          "ToothGrowth"                        
# [209] "validate_rowwise_df"                 "xpdrows.data.frame"
  • integrated help system, with executable examples
  • (for some packages) vignettes (typical problem, commands, and workflow)
  • CRAN Task Views: https://cran.r-project.org/web/views/
  • Books!
  • Courses!
  • Online: mailing lists, forums (StackOverflow, …), blogs, Twitter (#rstats)

2.3 Your starting vocabulary - a.k.a. Exercise Session 0

  • getwd() and setwd() - Tab is your friend
  • <-, =: the assignment operator
  • ls(), rm()
  • str()
  • example(), help()/?[function]
  • print()
  • q()/quit()
  • logical operators: TRUE,FALSE,!,==,!=,<,>,<=,>=,|,&,xor()
  • c()
  • data have help items too: e.g. cars

Find out what these do!

2.3.1 Exercise Session 0 - Solutions

?getwd
?setwd
?`<-`
help(ls)
help(rm)
?str
?example
help(help)
?print
help(quit)
?c
?cars

2.4 Make your life easier - Notes for your future self

  • add comments and document your own code
# This is a comment
  • write clean code - use spaces, indentation
  • use an editor with syntax highlighting/some form of autocompletion

Careful here:

  • R is case sensitive and has zero-tolerance with mis-spelled names
  • parenthesis: open and close them
  • special attention with missing values, factors VS strings: R is clever, but you might think differently
  • do not be stingy with parentheses - if this helps you
  • same goes with comments - your colleagues and your future self will thank you

2.5 Exercise session 1

Grab some mini-postit!

  • find out more about the iris dataset. What is it about at all? How many variables are included? How many observations?
  • replicate! find out a function that replicates elements of a vector to produce this
1 1 1 1

BONUS: … and this

1 2 3 4 5 1 2 3 4 5 1 2 3 4 5

2.5.1 Exercise Session 1 - Solutions

rep(1,4)
# [1] 1 1 1 1

rep(c(1,2,3,4,5),3)
#  [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5

2.6 Data types

R can recognize different general types of data

  • numbers (numeric)

  • character strings (text)

  • logical (e.g. class(TRUE))

  • factors (“integers with a set of labels”) - it is categorical data!

  • special ones: dates, time, …

When and where to use which?

2.7 I was curious about you

In a previous edition of a similar course, I wanted to know:

  • How old are you?
  • What is your current academic level? (PI, Postdoc, PhD, master)
  • What is your current knowledge level of R? (pro-good-intermediate-poor-none)
  • What is your knowledge of programming languages in general?
  • What is your experience level with genomics and RNA-seq data?
  • How familiar are you with mogon and parallel computing? (I am a regular user/Once in a while i used it/I know it exists/I heard we had some servers around/Is this supposed to be in the cloud?!)
  • What are your expectations from the course?

3 Bonus Step: reproducible reports

Our aims:

  • Understand what R Markdown is and why you should use it
  • Learn how to construct an R Markdown file
  • Export an R Markdown file into many file formats
  • –> You are all set to use Rmd to document any of your analyses!

3.1 Reproducible reports with R Markdown

R Markdown allows you to create documents that serve as a neat record of your analysis.

Why?

  • we want other researchers to easily understand what we did in our analysis, otherwise nobody can be certain that you analysed your data properly (yay, reproducible research!)
  • create an R markdown document as an appendix to a paper or project assignment, upload it to an online repository such as Github, or simply to keep as a personal record (future you will thank present you for this)

The key point is…

R Markdown documents present your code alongside its output (graphs, tables, etc.) with conventional text to explain it, a bit like a notebook. To do this, R Markdown uses markdown syntax.

3.2 Markdown

Markdown is a very simple markup language which provides methods for creating documents with headers, images, links etc. from plain text files, while keeping the original plain text file easy to read.

You can convert Markdown documents to many other file types like .html or .pdf to display the headers, images etc..

It might sound complicated. But really isn’t,

First things first: install the required software

  • R and RStudio (guess you have it already)
install.packages("rmarkdown")
library(rmarkdown)
  • knitr comes along, pandoc too. You should quickly be all set!

3.3 Basics of markdown

ABC here, let’s go through it:

http://rmarkdown.rstudio.com/authoring_basics.html

plus… a beautiful cheat sheet is there for you!

http://rmarkdown.rstudio.com/lesson-15.html

http://www.rstudio.com/wp-content/uploads/2016/03/rmarkdown-cheatsheet-2.0.pdf

Using LaTeX? No problem, you can use \(\LaTeX\) here as well!

\(\left( f(x) = \sum_{i=0}^{n} \frac{a_i}{1+x} \right)\)

What can you do with Rmarkdown?

http://rmarkdown.rstudio.com/gallery.html

http://rmarkdown.rstudio.com/formats.html

3.4 Let’s create one together!

3.4.1 Why is Rmd better than R

The price to pay to have an Rmd document is sooooo small - and for that, you get

  • code, text, output all together
  • one file only - no need to get lost
  • it even looks nice :)

3.4.2 Create an Rmarkdown file

To create a new R Markdown file (.Rmd), select File -> New File -> R Markdown... in RStudio, then choose the file type you want to create.

The newly created .Rmd file comes with basic instructions but we want to create our own R Markdown script, so let’s get to know the different parts of an Rmd file

  • An (optional) YAML header surrounded by ---s
  • R code chunks surrounded by backticks (```)
  • text mixed with simple text formatting

3.4.3 Inserting figures

Uh, you can insert figures also like this

![](images/grcat.png)

3.5 Insert text and code - any text, any code


```r
n <- 10
rnorm(n)
#  [1]  0.2750427  1.1800167  1.1070905  0.1732659 -0.2742060  1.5992610 -2.0094319 -0.7177109
#  [9]  0.4198595 -1.1974104
```

Shortcut: Ctrl + Alt + I

Input code: you can use multiple languages including R, Python, and SQL, many more (specify the language in the chunk options)

Inline code can be added with `r 1+1`

3.5.1 Chunk options

Deatiled very nicely here: https://yihui.name/knitr/options/

A simple set of options which you can use for many documents:

set.seed(42)
knitr::opts_chunk$set(
  comment = NA,
  fig.align = "center",
  fig.width = 7,
  fig.height = 7,
  warning = FALSE,
  eval = TRUE
)

3.5.2 Knit!

Use the Knit button in the RStudio IDE to render the file and preview the output with a single click or keyboard shortcut (Ctrl + Shift + K).

To generate a report from the file, run the render command (works also outside of RStudio):

library("rmarkdown")
rmarkdown::render("yourfile.Rmd")

It was a deep dive, but now…

  • You are familiar with the Markdown syntax and code chunk rules.
  • You can include figures and tables in your Markdown reports.
  • You can create R Markdown files and export them to pdf or html files.

3.7 Exercise session Bonus

  • create a new Rmarkdown document
  • can you find out how to generate a word document as output?
  • insert some code you previously used for exploring the small survey data - remember, a fresh session is run when knitting, so you need the commands from the very start!

3.7.1 Exercise Session Bonus - Solutions

  • File -> New File -> R Markdown... in RStudio
  • add this in the yaml header
output:
  word_document

4 Step 2: Data in, data out

4.1 Importing data in R

80-20? 90-10? Import, clean, prepare, transform your data

Sources:

  • Files, Clipboard, URL
  • Plain text file: Comma-separated, tab-delimited, …
  • R format file
  • SAS / Stata / SPSS file: package haven
  • Spreadsheet (Excel): package readxl - highly recommended!
  • Database: RSQLite, RPostgreSQL, RMySQL, …

4.2 The vocabulary of importing

… and exporting

  • read.table(),write.table() + read.csv|delim
  • the option stringsAsFactors=FALSE
  • load(),save()/readRDS(),saveRDS()
  • via haven : read_sas(),read_spss() /write_sas(),write_sav()
  • via readxl: read_excel()

Check out their documentation pages!

Other options: rio, RStudio GUI

4.3 Take a look at the data

Go to https://github.com/federicomarini/rbioc2016

-> inst/extdata

-> survey_responses.csv, in its raw format

You can load it directly like this

surveyrbioc <- read.csv("https://raw.githubusercontent.com/federicomarini/rbioc2016/master/inst/extdata/survey_responses.csv")

Or install the package and load it from there

library("devtools")
install_github("federicomarini/rbioc2016")
library("rbioc2016")
data(surveyrbioc)

4.4 Input data: Step by step, by hand?

Sometimes your data is either small and/or not in an Excel-like tabular format.

What to do? You combine the elements together!

Q1 <- c(28,27,33,32,29)
# should return this
Q1
[1] 28 27 33 32 29

Q2 <- c("PhD student","PhD student", "Postdoc","PhD student","PhD student")
Q2
[1] "PhD student" "PhD student" "Postdoc"     "PhD student" "PhD student"
# ... and so on

4.5 Combine the variables to a matrix

We have seen c(). We also have

  • cbind
  • rbind
firstTwo <- cbind(Q1,Q2)
firstTwo
     Q1   Q2           
[1,] "28" "PhD student"
[2,] "27" "PhD student"
[3,] "33" "Postdoc"    
[4,] "32" "PhD student"
[5,] "29" "PhD student"
rbind(Q1,Q2)
   [,1]          [,2]          [,3]      [,4]          [,5]         
Q1 "28"          "27"          "33"      "32"          "29"         
Q2 "PhD student" "PhD student" "Postdoc" "PhD student" "PhD student"

Is this what you wanted?

4.6 Applying the first functions

But first, what can you do on these objects?

sum(Q1)
[1] 149
sum(Q2)
Error in sum(Q2): invalid 'type' (character) of argument
summary(Q1)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   27.0    28.0    29.0    29.8    32.0    33.0 
summary(Q2)
   Length     Class      Mode 
        5 character character 
str(Q1)
 num [1:5] 28 27 33 32 29
str(Q2)
 chr [1:5] "PhD student" "PhD student" "Postdoc" "PhD student" "PhD student"
mean(Q1)
[1] 29.8
dim(firstTwo)
[1] 5 2
firstTwo[,1]
[1] "28" "27" "33" "32" "29"
mean(firstTwo[,1]) # Why, damn, why? Meet coercion
[1] NA
class(firstTwo)
[1] "matrix" "array" 

4.7 matrix, data.frame and list

  • a matrix can contain one type of data - if numeric, you unleash all the matrix algebra power!
  • a data.frame can store more types of data (one per column)
  • a list is like a big box where you can put anything - but this is not always what you want

What is best?

Let’s try with a list

Q3 <- c("intermediate","poor","good","none","intermediate")
mylist <- list(Q1,Q2,Q3)
mylist
[[1]]
[1] 28 27 33 32 29

[[2]]
[1] "PhD student" "PhD student" "Postdoc"     "PhD student" "PhD student"

[[3]]
[1] "intermediate" "poor"         "good"         "none"         "intermediate"
## access your elements with
mylist[[1]]
[1] 28 27 33 32 29
mylist[[1]][2]
[1] 27

How do we create a data.frame?

mydf <- data.frame(age = Q1,
                   level = Q2,
                   rexp = Q3)
mydf
  age       level         rexp
1  28 PhD student intermediate
2  27 PhD student         poor
3  33     Postdoc         good
4  32 PhD student         none
5  29 PhD student intermediate
class(mydf$age)
[1] "numeric"

4.7.1 Exploring a data.frame

mydf$age   # it's all about the money :)
[1] 28 27 33 32 29
mydf[,1]
[1] 28 27 33 32 29
names(mydf)
[1] "age"   "level" "rexp" 
rownames(mydf)
[1] "1" "2" "3" "4" "5"
dim(mydf)
[1] 5 3
nrow(mydf)
[1] 5
ncol(mydf)
[1] 3
surveyrbioc <- read.csv("https://raw.githubusercontent.com/federicomarini/rbioc2016/master/inst/extdata/survey_responses.csv")
head(surveyrbioc)
  Q1          Q2           Q3           Q4           Q5                                    Q6
1 28 PhD student intermediate intermediate         good                   I am a regular user
2 27 PhD student         poor intermediate         poor                      I know it exists
3 33     Postdoc         good         good         good                      I know it exists
4 32 PhD student         poor         poor         poor Is this supposed to be in the cloud?!
5 29 PhD student         none         poor         poor                      I know it exists
6 33     Postdoc intermediate intermediate intermediate             Once in a while i used it
                                                                                                                                                                                                                                              Q7
1                                                                                                                                                                                                Learn Parallelization with R (and Bioconductor)
2                                                                                                                                                                                                                                           <NA>
3                                                                                                                                                                                  use R scripts in parallel context (ex: alignments in RNA-seq)
4                                                                Id like to gather practise to analyse count data and develope the necessary data design. Additionally, it would be interesting to get to now how to find putative novel miRNAs.
5                                                                                                                                                                                                 Possible I will use R for editing RNA-seq data
6 I would describe myself as an advanced beginner, I am actively using R now to look (mostly) at count tables from NGS data and make plots. I am especially interested in the Bioconductor and parallelization in R section, this is new for me.
tail(surveyrbioc)
   Q1                  Q2           Q3           Q4           Q5
17 28 Master student/else intermediate intermediate intermediate
18 39             Postdoc         good intermediate         good
19 32 Master student/else         none         poor    genoWhat?
20 29         PhD student         poor         none         poor
21 31         PhD student         none         none         good
22 30         PhD student         none         none         good
                                      Q6                                                         Q7
17                      I know it exists       better understanding of R and to extend my knowledge
18                   I am a regular user            Mainly interested in parallel computing options
19 Is this supposed to be in the cloud?!                                                       <NA>
20 Is this supposed to be in the cloud?!          To better understand R and perform basic analysis
21    I heard we had some servers around                        working with fastq files based on R
22 Is this supposed to be in the cloud?! I want to be able to analyze my sequence data on my own :P
names(surveyrbioc)
[1] "Q1" "Q2" "Q3" "Q4" "Q5" "Q6" "Q7"
# View(surveyrbioc)
str(surveyrbioc)
'data.frame':   22 obs. of  7 variables:
 $ Q1: int  28 27 33 32 29 33 40 23 27 23 ...
 $ Q2: chr  "PhD student" "PhD student" "Postdoc" "PhD student" ...
 $ Q3: chr  "intermediate" "poor" "good" "poor" ...
 $ Q4: chr  "intermediate" "intermediate" "good" "poor" ...
 $ Q5: chr  "good" "poor" "good" "poor" ...
 $ Q6: chr  "I am a regular user" "I know it exists" "I know it exists" "Is this supposed to be in the cloud?!" ...
 $ Q7: chr  "Learn Parallelization with R (and Bioconductor)" NA "use R scripts in parallel context (ex: alignments in RNA-seq)" "Id like to gather practise to analyse count data and develope the necessary data design. Additionally, it would"| __truncated__ ...
summary(surveyrbioc)
       Q1             Q2                 Q3                 Q4                 Q5           
 Min.   :23.00   Length:22          Length:22          Length:22          Length:22         
 1st Qu.:27.25   Class :character   Class :character   Class :character   Class :character  
 Median :29.50   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
 Mean   :30.14                                                                              
 3rd Qu.:32.75                                                                              
 Max.   :40.00                                                                              
      Q6                 Q7           
 Length:22          Length:22         
 Class :character   Class :character  
 Mode  :character   Mode  :character  
                                      
                                      
                                      
surveyrbioc[, ]
   Q1                  Q2           Q3           Q4           Q5
1  28         PhD student intermediate intermediate         good
2  27         PhD student         poor intermediate         poor
3  33             Postdoc         good         good         good
4  32         PhD student         poor         poor         poor
5  29         PhD student         none         poor         poor
6  33             Postdoc intermediate intermediate intermediate
7  40             Postdoc         good         good intermediate
8  23 Master student/else         poor         poor         good
9  27         PhD student         none         poor intermediate
10 23 Master student/else         poor         poor         poor
11 35             Postdoc         poor         poor intermediate
12 34 Master student/else         poor          pro         poor
13 31             Postdoc         none         none intermediate
14 27         PhD student         none         poor         poor
15 24 Master student/else         poor intermediate intermediate
16 28         PhD student         none         poor         poor
17 28 Master student/else intermediate intermediate intermediate
18 39             Postdoc         good intermediate         good
19 32 Master student/else         none         poor    genoWhat?
20 29         PhD student         poor         none         poor
21 31         PhD student         none         none         good
22 30         PhD student         none         none         good
                                      Q6
1                    I am a regular user
2                       I know it exists
3                       I know it exists
4  Is this supposed to be in the cloud?!
5                       I know it exists
6              Once in a while i used it
7                    I am a regular user
8                       I know it exists
9                       I know it exists
10 Is this supposed to be in the cloud?!
11                      I know it exists
12                      I know it exists
13 Is this supposed to be in the cloud?!
14                      I know it exists
15                      I know it exists
16                      I know it exists
17                      I know it exists
18                   I am a regular user
19 Is this supposed to be in the cloud?!
20 Is this supposed to be in the cloud?!
21    I heard we had some servers around
22 Is this supposed to be in the cloud?!
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         Q7
1                                                                                                                                                                                                                                                                                                                                                                                                                                           Learn Parallelization with R (and Bioconductor)
2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      <NA>
3                                                                                                                                                                                                                                                                                                                                                                                                                             use R scripts in parallel context (ex: alignments in RNA-seq)
4                                                                                                                                                                                                                                                                                                           Id like to gather practise to analyse count data and develope the necessary data design. Additionally, it would be interesting to get to now how to find putative novel miRNAs.
5                                                                                                                                                                                                                                                                                                                                                                                                                                            Possible I will use R for editing RNA-seq data
6                                                                                                                                                                                                                                            I would describe myself as an advanced beginner, I am actively using R now to look (mostly) at count tables from NGS data and make plots. I am especially interested in the Bioconductor and parallelization in R section, this is new for me.
7                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      <NA>
8                                                                                                                                                                                                                                                                                                                                                                                                                                                      Get to know R better, basic commands
9                                                                                                                                                                                                                                                                                                                                                                                                                             To look if I can process my sequencing data on my own using R
10 I would like to learn more about R, especially how to use it in biomedial research. Im in the 2nd Semester of the Rasterprogramm biomedicine, last Semester I had two weeks bioinformatics and we used to work with R for statistics/ChIP-Seq/microarray-data analysis, but the time was too short, to go deep into it, we just scratched the surface. So now I wish to learn some more, would be great, if I could work with the programme by myself, for example for the masterthesis.
11                                                                                                                                                                                                                                                                                                                                                                                                                                                                       learn more about R
12                                                                                                                                                                                                                                                                                                                                                                                               Brush up on some R knowledge and maybe get some different perspective on Genome processing
13                                                                                                                                                                                                                                                                                                                                                                                                      to get a good and understandable introduction into R programming and bioinformatics
14                                                                                                                                                                                                                                                                                                                                                                                                                                                              learning how to work with R
15                                                                                                                                                                                                                                                                                                                                                                                                                                                     To learn the basics of R programming
16                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     <NA>
17                                                                                                                                                                                                                                                                                                                                                                                                                                     better understanding of R and to extend my knowledge
18                                                                                                                                                                                                                                                                                                                                                                                                                                          Mainly interested in parallel computing options
19                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     <NA>
20                                                                                                                                                                                                                                                                                                                                                                                                                                        To better understand R and perform basic analysis
21                                                                                                                                                                                                                                                                                                                                                                                                                                                      working with fastq files based on R
22                                                                                                                                                                                                                                                                                                                                                                                                                               I want to be able to analyze my sequence data on my own :P

4.8 Exercise session 2

Using the surveyrbioc object:

  • Calculate the mean age of the participants
  • How many participants did actually take part to the survey?
  • How old was the oldest participant? (max can be your help)
  • transpose the survey data and assign it to another variable
  • Change the column names of this object and save this data set as a tab-separated ASCII file
  • BONUS: what was the youngest participant expecting?

4.8.1 Exercise Session 2 - Solutions

mean(surveyrbioc$Q1)
[1] 30.13636

max(surveyrbioc$Q1)
[1] 40

my_transposed_survey <- t(surveyrbioc)

surveyrbioc_mod <- surveyrbioc
colnames(surveyrbioc_mod) <- c("age","level","rlevel","prog_level","genomics_level","parcomp_level","expectation")

surveyrbioc_mod$expectation[which.min(surveyrbioc_mod$age)]
[1] "Get to know R better, basic commands"

5 Step 3: Analyzing (tabular) data

Describe, explore, transform, summarise data

5.1 Exploring, subsetting, manipulating, analysing

  • dim(x) shows the dimensions of an object
  • str(x) provides an overview of the structure of an object and the elements it contains
  • sum(x), mean(x), sd(x) computes the sum, mean, or standard deviation of all the elements in x; median(x), quantile(x)
  • length(x) returns the number of elements in x (a vector)
  • sqrt(x), log(x) take the square root and the natural logarithm of a numeric - element or vector
  • hist(x, breaks=20, col="blue") plots a histogram of variable x with 20 bins colored blue
  • unique(x) returns the vector of unique elements in x
  • rm(x) removes the object x from the environment (rm(list=ls()) removes all objects)
  • sessionInfo() prints information about R session and versions of all attached packages
  • logical operators might often come handy!

5.2 Subsetting the data

This is the basic way it works

surveyrbioc[ROWS,COLUMNS]

You can subset with…

  • integers
  • blank spaces
  • names
  • logical vectors

Try to make a guess, given this vector.

vec <- c(6, 1, 3, 6, 10, 5)

What happens if you do this?

vec[2]
[1] 1
vec[c(5, 6)]
[1] 10  5
vec[-c(5,6)]
[1] 6 1 3 6
vec > 5
[1]  TRUE FALSE FALSE  TRUE  TRUE FALSE
vec[vec > 5]
[1]  6  6 10

What happens if you do this?

df <- data.frame(
  name = c("John", "Paul", "George", "Ringo"),
  birth = c(1940, 1942, 1943, 1940),
  instrument = c("guitar", "bass", "guitar", "drums")
)

df
    name birth instrument
1   John  1940     guitar
2   Paul  1942       bass
3 George  1943     guitar
4  Ringo  1940      drums

df[c(2, 4), 3]
[1] "bass"  "drums"
df[ , 1]
[1] "John"   "Paul"   "George" "Ringo" 
df[ , "instrument"]
[1] "guitar" "bass"   "guitar" "drums" 
df$instrument
[1] "guitar" "bass"   "guitar" "drums" 

Back to the survey

# I just want the age
surveyrbioc[,1] 
 [1] 28 27 33 32 29 33 40 23 27 23 35 34 31 27 24 28 28 39 32 29 31 30
# or
surveyrbioc$Q1
 [1] 28 27 33 32 29 33 40 23 27 23 35 34 31 27 24 28 28 39 32 29 31 30

# the first 4 columns
surveyrbioc[,c(1,2,3,4)]
   Q1                  Q2           Q3           Q4
1  28         PhD student intermediate intermediate
2  27         PhD student         poor intermediate
3  33             Postdoc         good         good
4  32         PhD student         poor         poor
5  29         PhD student         none         poor
6  33             Postdoc intermediate intermediate
7  40             Postdoc         good         good
8  23 Master student/else         poor         poor
9  27         PhD student         none         poor
10 23 Master student/else         poor         poor
11 35             Postdoc         poor         poor
12 34 Master student/else         poor          pro
13 31             Postdoc         none         none
14 27         PhD student         none         poor
15 24 Master student/else         poor intermediate
16 28         PhD student         none         poor
17 28 Master student/else intermediate intermediate
18 39             Postdoc         good intermediate
19 32 Master student/else         none         poor
20 29         PhD student         poor         none
21 31         PhD student         none         none
22 30         PhD student         none         none
surveyrbioc[,1:4]
   Q1                  Q2           Q3           Q4
1  28         PhD student intermediate intermediate
2  27         PhD student         poor intermediate
3  33             Postdoc         good         good
4  32         PhD student         poor         poor
5  29         PhD student         none         poor
6  33             Postdoc intermediate intermediate
7  40             Postdoc         good         good
8  23 Master student/else         poor         poor
9  27         PhD student         none         poor
10 23 Master student/else         poor         poor
11 35             Postdoc         poor         poor
12 34 Master student/else         poor          pro
13 31             Postdoc         none         none
14 27         PhD student         none         poor
15 24 Master student/else         poor intermediate
16 28         PhD student         none         poor
17 28 Master student/else intermediate intermediate
18 39             Postdoc         good intermediate
19 32 Master student/else         none         poor
20 29         PhD student         poor         none
21 31         PhD student         none         none
22 30         PhD student         none         none

# all but the last column
surveyrbioc[,-7]
   Q1                  Q2           Q3           Q4           Q5
1  28         PhD student intermediate intermediate         good
2  27         PhD student         poor intermediate         poor
3  33             Postdoc         good         good         good
4  32         PhD student         poor         poor         poor
5  29         PhD student         none         poor         poor
6  33             Postdoc intermediate intermediate intermediate
7  40             Postdoc         good         good intermediate
8  23 Master student/else         poor         poor         good
9  27         PhD student         none         poor intermediate
10 23 Master student/else         poor         poor         poor
11 35             Postdoc         poor         poor intermediate
12 34 Master student/else         poor          pro         poor
13 31             Postdoc         none         none intermediate
14 27         PhD student         none         poor         poor
15 24 Master student/else         poor intermediate intermediate
16 28         PhD student         none         poor         poor
17 28 Master student/else intermediate intermediate intermediate
18 39             Postdoc         good intermediate         good
19 32 Master student/else         none         poor    genoWhat?
20 29         PhD student         poor         none         poor
21 31         PhD student         none         none         good
22 30         PhD student         none         none         good
                                      Q6
1                    I am a regular user
2                       I know it exists
3                       I know it exists
4  Is this supposed to be in the cloud?!
5                       I know it exists
6              Once in a while i used it
7                    I am a regular user
8                       I know it exists
9                       I know it exists
10 Is this supposed to be in the cloud?!
11                      I know it exists
12                      I know it exists
13 Is this supposed to be in the cloud?!
14                      I know it exists
15                      I know it exists
16                      I know it exists
17                      I know it exists
18                   I am a regular user
19 Is this supposed to be in the cloud?!
20 Is this supposed to be in the cloud?!
21    I heard we had some servers around
22 Is this supposed to be in the cloud?!
# if you don't know we had 7 columns...
surveyrbioc[,-ncol(surveyrbioc)]
   Q1                  Q2           Q3           Q4           Q5
1  28         PhD student intermediate intermediate         good
2  27         PhD student         poor intermediate         poor
3  33             Postdoc         good         good         good
4  32         PhD student         poor         poor         poor
5  29         PhD student         none         poor         poor
6  33             Postdoc intermediate intermediate intermediate
7  40             Postdoc         good         good intermediate
8  23 Master student/else         poor         poor         good
9  27         PhD student         none         poor intermediate
10 23 Master student/else         poor         poor         poor
11 35             Postdoc         poor         poor intermediate
12 34 Master student/else         poor          pro         poor
13 31             Postdoc         none         none intermediate
14 27         PhD student         none         poor         poor
15 24 Master student/else         poor intermediate intermediate
16 28         PhD student         none         poor         poor
17 28 Master student/else intermediate intermediate intermediate
18 39             Postdoc         good intermediate         good
19 32 Master student/else         none         poor    genoWhat?
20 29         PhD student         poor         none         poor
21 31         PhD student         none         none         good
22 30         PhD student         none         none         good
                                      Q6
1                    I am a regular user
2                       I know it exists
3                       I know it exists
4  Is this supposed to be in the cloud?!
5                       I know it exists
6              Once in a while i used it
7                    I am a regular user
8                       I know it exists
9                       I know it exists
10 Is this supposed to be in the cloud?!
11                      I know it exists
12                      I know it exists
13 Is this supposed to be in the cloud?!
14                      I know it exists
15                      I know it exists
16                      I know it exists
17                      I know it exists
18                   I am a regular user
19 Is this supposed to be in the cloud?!
20 Is this supposed to be in the cloud?!
21    I heard we had some servers around
22 Is this supposed to be in the cloud?!

# you can subset with logical vectors, by row and by column
surveyrbioc[c(rep(TRUE,10),rep(FALSE,8)),]
   Q1                  Q2           Q3           Q4           Q5
1  28         PhD student intermediate intermediate         good
2  27         PhD student         poor intermediate         poor
3  33             Postdoc         good         good         good
4  32         PhD student         poor         poor         poor
5  29         PhD student         none         poor         poor
6  33             Postdoc intermediate intermediate intermediate
7  40             Postdoc         good         good intermediate
8  23 Master student/else         poor         poor         good
9  27         PhD student         none         poor intermediate
10 23 Master student/else         poor         poor         poor
19 32 Master student/else         none         poor    genoWhat?
20 29         PhD student         poor         none         poor
21 31         PhD student         none         none         good
22 30         PhD student         none         none         good
                                      Q6
1                    I am a regular user
2                       I know it exists
3                       I know it exists
4  Is this supposed to be in the cloud?!
5                       I know it exists
6              Once in a while i used it
7                    I am a regular user
8                       I know it exists
9                       I know it exists
10 Is this supposed to be in the cloud?!
19 Is this supposed to be in the cloud?!
20 Is this supposed to be in the cloud?!
21    I heard we had some servers around
22 Is this supposed to be in the cloud?!
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         Q7
1                                                                                                                                                                                                                                                                                                                                                                                                                                           Learn Parallelization with R (and Bioconductor)
2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      <NA>
3                                                                                                                                                                                                                                                                                                                                                                                                                             use R scripts in parallel context (ex: alignments in RNA-seq)
4                                                                                                                                                                                                                                                                                                           Id like to gather practise to analyse count data and develope the necessary data design. Additionally, it would be interesting to get to now how to find putative novel miRNAs.
5                                                                                                                                                                                                                                                                                                                                                                                                                                            Possible I will use R for editing RNA-seq data
6                                                                                                                                                                                                                                            I would describe myself as an advanced beginner, I am actively using R now to look (mostly) at count tables from NGS data and make plots. I am especially interested in the Bioconductor and parallelization in R section, this is new for me.
7                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      <NA>
8                                                                                                                                                                                                                                                                                                                                                                                                                                                      Get to know R better, basic commands
9                                                                                                                                                                                                                                                                                                                                                                                                                             To look if I can process my sequencing data on my own using R
10 I would like to learn more about R, especially how to use it in biomedial research. Im in the 2nd Semester of the Rasterprogramm biomedicine, last Semester I had two weeks bioinformatics and we used to work with R for statistics/ChIP-Seq/microarray-data analysis, but the time was too short, to go deep into it, we just scratched the surface. So now I wish to learn some more, would be great, if I could work with the programme by myself, for example for the masterthesis.
19                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     <NA>
20                                                                                                                                                                                                                                                                                                                                                                                                                                        To better understand R and perform basic analysis
21                                                                                                                                                                                                                                                                                                                                                                                                                                                      working with fastq files based on R
22                                                                                                                                                                                                                                                                                                                                                                                                                               I want to be able to analyze my sequence data on my own :P
surveyrbioc[c(TRUE,FALSE),] # keep in mind this behavior!
   Q1                  Q2           Q3           Q4           Q5
1  28         PhD student intermediate intermediate         good
3  33             Postdoc         good         good         good
5  29         PhD student         none         poor         poor
7  40             Postdoc         good         good intermediate
9  27         PhD student         none         poor intermediate
11 35             Postdoc         poor         poor intermediate
13 31             Postdoc         none         none intermediate
15 24 Master student/else         poor intermediate intermediate
17 28 Master student/else intermediate intermediate intermediate
19 32 Master student/else         none         poor    genoWhat?
21 31         PhD student         none         none         good
                                      Q6
1                    I am a regular user
3                       I know it exists
5                       I know it exists
7                    I am a regular user
9                       I know it exists
11                      I know it exists
13 Is this supposed to be in the cloud?!
15                      I know it exists
17                      I know it exists
19 Is this supposed to be in the cloud?!
21    I heard we had some servers around
                                                                                    Q7
1                                      Learn Parallelization with R (and Bioconductor)
3                        use R scripts in parallel context (ex: alignments in RNA-seq)
5                                       Possible I will use R for editing RNA-seq data
7                                                                                 <NA>
9                        To look if I can process my sequencing data on my own using R
11                                                                  learn more about R
13 to get a good and understandable introduction into R programming and bioinformatics
15                                                To learn the basics of R programming
17                                better understanding of R and to extend my knowledge
19                                                                                <NA>
21                                                 working with fastq files based on R

# guess what this does?
surveyrbioc$Q2=="PhD student"
 [1]  TRUE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE
[17] FALSE FALSE FALSE  TRUE  TRUE  TRUE

5.3 Exercise session 3

  • How many PhD students did reply?
  • What is the proportion of PhD students to all other participants?
  • How old are they on average?
  • How many of the participants are older than 30?
  • How many postdocs are younger than 35?
  • How many of the participants did not reply to the last question?

5.3.1 Exercise Session 3 - Solutions

sum(surveyrbioc$Q2 == "PhD student")
[1] 10
mean(surveyrbioc$Q2 == "PhD student")
[1] 0.4545455
mean(surveyrbioc$Q1[surveyrbioc$Q2 == "PhD student"])
[1] 28.8
sum(surveyrbioc$Q1 >= 30)
[1] 11
sum(surveyrbioc$Q1 < 35 & surveyrbioc$Q2 == "Postdoc")
[1] 3
sum(is.na(surveyrbioc$Q7))
[1] 4

5.4 Manipulating and analysing your data

You can

  • sort the data (see sort and order)
  • transform your data: apply rules (formulas, logics, insight altogether)
  • combine two datasets or more (if you merge them)
  • do some statistics on your data

5.5 Sorting the data

myord <- order(surveyrbioc$Q1)
myord
 [1]  8 10 15  2  9 14  1 16 17  5 20 22 13 21  4 19  3  6 12 11 18  7

head(surveyrbioc[myord,1:5],4)
   Q1                  Q2   Q3           Q4           Q5
8  23 Master student/else poor         poor         good
10 23 Master student/else poor         poor         poor
15 24 Master student/else poor intermediate intermediate
2  27         PhD student poor intermediate         poor
sorted_surv <- surveyrbioc[myord,1:6]

sort() returns you the sorted data, order() the indices only

5.6 Transforming the data

# transforming a variable
newsurvey <- surveyrbioc[,1:5]
newsurvey$ageroot <- sqrt(newsurvey$Q1)
head(newsurvey)
  Q1          Q2           Q3           Q4           Q5  ageroot
1 28 PhD student intermediate intermediate         good 5.291503
2 27 PhD student         poor intermediate         poor 5.196152
3 33     Postdoc         good         good         good 5.744563
4 32 PhD student         poor         poor         poor 5.656854
5 29 PhD student         none         poor         poor 5.385165
6 33     Postdoc intermediate intermediate intermediate 5.744563

# creating groups out of a continuous variable
newsurvey$agegroup <- cut(newsurvey$Q1,breaks = c(20,30,40))
head(newsurvey)
  Q1          Q2           Q3           Q4           Q5  ageroot agegroup
1 28 PhD student intermediate intermediate         good 5.291503  (20,30]
2 27 PhD student         poor intermediate         poor 5.196152  (20,30]
3 33     Postdoc         good         good         good 5.744563  (30,40]
4 32 PhD student         poor         poor         poor 5.656854  (30,40]
5 29 PhD student         none         poor         poor 5.385165  (20,30]
6 33     Postdoc intermediate intermediate intermediate 5.744563  (30,40]

Use case for merge: you have two sets you are playing with! Think in advance what you need for that purpose…

5.7 We want statistics!

Are PhD students significantly younger than postdocs? Are there any differences in the age of the three groups?

phds <- surveyrbioc[surveyrbioc$Q2=="PhD student",]
postdocs <- surveyrbioc[surveyrbioc$Q2=="Postdoc",]
t.test(phds$Q1,postdocs$Q1)

    Welch Two Sample t-test

data:  phds$Q1 and postdocs$Q1
t = -4.0528, df = 6.4476, p-value = 0.005767
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -10.146796  -2.586537
sample estimates:
mean of x mean of y 
 28.80000  35.16667 
aov(data=surveyrbioc,Q1~Q2) # What is missing here?
Call:
   aov(formula = Q1 ~ Q2, data = surveyrbioc)

Terms:
                      Q2 Residuals
Sum of Squares  216.8242  207.7667
Deg. of Freedom        2        19

Residual standard error: 3.306824
Estimated effects may be unbalanced

Much more on this: in the next courses!

5.8 Simple yet powerful functions

tapply

You want to calculate the median age of each academic group in here

md <- median(surveyrbioc$Q1)
md_master <- median(surveyrbioc$Q1[surveyrbioc$Q2=="Master student/else"])
md_phd <- median(surveyrbioc$Q1[surveyrbioc$Q2=="PhD student"])
md_postdocs <- median(surveyrbioc$Q1[surveyrbioc$Q2=="Postdoc"])
c(md_master,md_phd,md_postdocs)
[1] 26.0 28.5 34.0

tapply splits the data of the first variable on the levels of the second variable, and applies the function (any function)

tapply(X = surveyrbioc$Q1,INDEX = surveyrbioc$Q2,FUN = median)
Master student/else         PhD student             Postdoc 
               26.0                28.5                34.0 

lapply and sapply

Back to our iris dataset

names(iris)
[1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"     

We want the average sepal length and width, and the same for the petals. Uh, and we want the standard deviation too.

# the unefficient way:
seplen_m <- mean(iris$Sepal.Length)
sepwid_m <- mean(iris$Sepal.Width)
petlen_m <- mean(iris$Petal.Length)
petwid_m <- mean(iris$Petal.Width)

seplen_m <- sd(iris$Sepal.Length)
# ... and so on

-> Apply a Function over a List or Vector

# we will use just the first four columns
lapply(iris[,1:4],mean)
$Sepal.Length
[1] 5.843333

$Sepal.Width
[1] 3.057333

$Petal.Length
[1] 3.758

$Petal.Width
[1] 1.199333
sapply(iris[,1:4],mean)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333 
lapply(iris[,1:4],sd)
$Sepal.Length
[1] 0.8280661

$Sepal.Width
[1] 0.4358663

$Petal.Length
[1] 1.765298

$Petal.Width
[1] 0.7622377
# ...

The major difference is in the presentation of the output

summary

Try out summary on a data.frame

summary(iris)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300   versicolor:50  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300   virginica :50  
 Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199                  
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800                  
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500                  

Alternatives in other packages:

  • describe() in the Hmisc package
  • skim() from skimr
  • create_report() from Data Explorer

table

table(surveyrbioc$Q3)

        good intermediate         none         poor 
           3            3            8            8 
table(surveyrbioc$Q4)

        good intermediate         none         poor          pro 
           2            6            4            9            1 

table(surveyrbioc$Q2,surveyrbioc$Q3)
                     
                      good intermediate none poor
  Master student/else    0            1    1    4
  PhD student            0            1    6    3
  Postdoc                3            1    1    1
  • want the sums? Try addmargins()
  • looking for the percentage values? prop.table()
  • somewhat nicer output: ftable()
addmargins(table(surveyrbioc$Q2,surveyrbioc$Q3))
                     
                      good intermediate none poor Sum
  Master student/else    0            1    1    4   6
  PhD student            0            1    6    3  10
  Postdoc                3            1    1    1   6
  Sum                    3            3    8    8  22
prop.table(table(surveyrbioc$Q2,surveyrbioc$Q3))
                     
                            good intermediate       none       poor
  Master student/else 0.00000000   0.04545455 0.04545455 0.18181818
  PhD student         0.00000000   0.04545455 0.27272727 0.13636364
  Postdoc             0.13636364   0.04545455 0.04545455 0.04545455

Please always do check the docs!

5.9 Exercise session 4

The MASS package contains the dataset Cars93, which stores the data on 93 makes of car sold in US

  • you’ll need the package and the data
  • Type specifies the type of market the car is aimed at. Find the cheapest car in each type, and the one with the greatest fuel efficiency
  • compute the mean horsepower for each type
  • create two data.frames, one for US cars, the other one with non-US cars
  • export the US cars to a text file
  • save the non-US cars data to a binary file (.RData)

5.9.1 Exercise Session 4 - Solutions

library(MASS)
head(Cars93)
  Manufacturer   Model    Type Min.Price Price Max.Price MPG.city MPG.highway            AirBags
1        Acura Integra   Small      12.9  15.9      18.8       25          31               None
2        Acura  Legend Midsize      29.2  33.9      38.7       18          25 Driver & Passenger
3         Audi      90 Compact      25.9  29.1      32.3       20          26        Driver only
4         Audi     100 Midsize      30.8  37.7      44.6       19          26 Driver & Passenger
5          BMW    535i Midsize      23.7  30.0      36.2       22          30        Driver only
6        Buick Century Midsize      14.2  15.7      17.3       22          31        Driver only
  DriveTrain Cylinders EngineSize Horsepower  RPM Rev.per.mile Man.trans.avail Fuel.tank.capacity
1      Front         4        1.8        140 6300         2890             Yes               13.2
2      Front         6        3.2        200 5500         2335             Yes               18.0
3      Front         6        2.8        172 5500         2280             Yes               16.9
4      Front         6        2.8        172 5500         2535             Yes               21.1
5       Rear         4        3.5        208 5700         2545             Yes               21.1
6      Front         4        2.2        110 5200         2565              No               16.4
  Passengers Length Wheelbase Width Turn.circle Rear.seat.room Luggage.room Weight  Origin
1          5    177       102    68          37           26.5           11   2705 non-USA
2          5    195       115    71          38           30.0           15   3560 non-USA
3          5    180       102    67          37           28.0           14   3375 non-USA
4          6    193       106    70          37           31.0           17   3405 non-USA
5          4    186       109    69          39           27.0           13   3640 non-USA
6          6    189       105    69          41           28.0           16   2880     USA
           Make
1 Acura Integra
2  Acura Legend
3       Audi 90
4      Audi 100
5      BMW 535i
6 Buick Century
?Cars93
tapply(X = Cars93$Min.Price,INDEX = Cars93$Type,FUN = min)
Compact   Large Midsize   Small  Sporty     Van 
    8.5    17.5    12.4     6.7     9.1    13.6 
tapply(X = Cars93$Horsepower,INDEX = Cars93$Type,FUN = mean)
 Compact    Large  Midsize    Small   Sporty      Van 
131.0000 179.4545 173.0909  91.0000 160.1429 149.4444 

table(Cars93$Origin)

    USA non-USA 
     48      45 
us_cars <- Cars93[Cars93$Origin == "USA",]
nonus_cars <- Cars93[Cars93$Origin != "USA",]
# write.csv(us_cars, file = "us_cars.csv")
# save(nonus_cars, file = "nonus_cars.RData")

6 Step 4: Plotting data

6.1 Graphics in R

  • powerful environment for visualizing scientific data
  • integrated graphics AND statistics
  • publication-ready quality
  • fully programmable, highly reproducible

Many ways for the same task:

  • base graphics (plot)
  • ggplot2
  • lattice
  • interactive visualizations such as plotly, ggvis or other libraries

Why bother plotting at all?

  • facilitate comparisons
  • identify trends
  • generate hypotheses

6.2 You can do all this with R

Look for some inspiration here, it is an excellent place to start and learn!

https://r-graph-gallery.com/

6.3 The plot function

First thing: take a look at the overview documentation of plot

?plot

We will see

  • scatter plots
  • boxplots
  • barplots
  • histograms

6.4 plot parameters

Required:

  • x variable
  • y variable

Other options

  • title with main
  • axes labels with xlab and ylab
  • axes limits with xlim and ylim
  • symbols, colors and sizes: pch, col and cex - as atomic elements or as vectors

6.5 Get to know the data: mpg

library(ggplot2) # this is useful per se, and contains the dataset we will be using
?mpg
This dataset contains a subset of the fuel economy data that the EPA makes available on http://fueleconomy.gov
# works on RStudio
# View(mpg)
# otherwise stick to the classic
str(mpg)
tibble [234 × 12] (S3: tbl_df/tbl/data.frame)
 $ manufacturer: chr [1:234] "audi" "audi" "audi" "audi" ...
 $ model       : chr [1:234] "a4" "a4" "a4" "a4" ...
 $ displ       : num [1:234] 1.8 1.8 2 2 2.8 2.8 3.1 1.8 1.8 2 ...
 $ year        : int [1:234] 1999 1999 2008 2008 1999 1999 2008 1999 1999 2008 ...
 $ cyl         : int [1:234] 4 4 4 4 6 6 6 4 4 4 ...
 $ trans       : chr [1:234] "auto(l5)" "manual(m5)" "manual(m6)" "auto(av)" ...
 $ drv         : chr [1:234] "f" "f" "f" "f" ...
 $ cty         : int [1:234] 18 21 20 21 16 18 18 18 16 20 ...
 $ hwy         : int [1:234] 29 29 31 30 26 26 27 26 25 28 ...
 $ fl          : chr [1:234] "p" "p" "p" "p" ...
 $ class       : chr [1:234] "compact" "compact" "compact" "compact" ...
 $ mygroup     : num [1:234] 2 2 2 2 2 2 2 2 2 2 ...

Make a guess: what do you expect to see between fuel consumption and engine size?

6.6 Scatter plots

plot(mpg$displ,mpg$cty)

Bonus: what is the correlation?

cor(mpg$displ,mpg$cty)
[1] -0.798524
cor(mpg$displ,mpg$cty,method="spearman")
[1] -0.8809049

6.6.1 Can we do more?

mpg$mygroup <- as.numeric(factor(mpg$class))
plot(mpg$displ,mpg$cty,
     col = mpg$mygroup)
legend("topright",legend = levels(factor(mpg$class)),col=levels(factor(mpg$mygroup)),pch=1)

plot(mpg$displ,mpg$cty,
     pch = as.numeric(factor((mpg$class))))

This shows we have quite some overlap of points. What can we do?

Adding some jitter…

plot(x = mpg$displ + rnorm(nrow(mpg),mean = 0,sd = 0.01),
     y = mpg$cty + rnorm(rnorm(nrow(mpg),mean = 0,sd = 0.01)),
     col = mpg$mygroup,
     main = "now with jitter!")

Adding a smoothing line

Trying to see a pattern? Add a smoothing curve.

This one is wrong - missing the reordering of points

plot(mpg$displ,mpg$cty, col = mpg$mygroup)
myloess <- loess(cty~displ, data=mpg)
myfit <- fitted(myloess)
lines(mpg$displ,myfit)
legend("topright",legend = levels(factor(mpg$class)),col=levels(factor(mpg$mygroup)),pch=1)

This one is correct!

plot(mpg$displ,mpg$cty, col = mpg$mygroup)
myloess <- loess(cty~displ, data=mpg)
myfit <- fitted(myloess)
myord <- order(mpg$displ)
lines(mpg$displ[myord],myfit[myord])
legend("topright",legend = levels(factor(mpg$class)),col=levels(factor(mpg$mygroup)),pch=1)

lines can add (almost) anything (any line).

points works in a similar way to superimpose, well, points

6.7 Bar charts

?barplot
academia_levels <- table(surveyrbioc$Q2)
barplot(academia_levels)

6.8 Boxplots

How is the age distributed across academic levels? Check the help of boxplot

  • A formula is required!
  • Don’t worry, it’s nothing but your y~x variables - ok, it can get more complicated
boxplot(Q1~Q2,
        data = surveyrbioc)

Splitting on more factors

boxplot(Q1~Q2+Q3,
        data = surveyrbioc)

Making it more readable…

boxplot(Q1~Q2+Q3,
        data = surveyrbioc,
        las = 2)

Changing the parameters allows you to control many aspects on plot appearance par is your best friend - and enemy (see ?par)

par(mar=c(15,3,2,2))
boxplot(Q1~Q2+Q3,data = surveyrbioc,las = 2)

par( ... ) has many arguments; here, the useful/most used ones

  • mar for handling the margins
  • cex, col, pch and co. are all parameters of par
  • las to change the style of the axis labels
  • mfrow to draw an array of figures

6.9 Histograms

hist(surveyrbioc$Q1,breaks = 8)

6.10 More histograms!

hist(mpg$cty,breaks = 10)

hist(mpg$cty,breaks = 10, col = "steelblue")

hist(mpg$cty,breaks = 10, col = "steelblue", border = "gray")

hist(mpg$cty,breaks = 10, col = "steelblue", border = "gray",main = "Distribution of miles/gallon consumption in city traffic")

6.11 How to do nice pie charts

DON’T.

If you really need to do it…

?pie
example(pie) # expecially the last one

pie> require(grDevices)

pie> pie(rep(1, 24), col = rainbow(24), radius = 0.9)


pie> pie.sales <- c(0.12, 0.3, 0.26, 0.16, 0.04, 0.12)

pie> names(pie.sales) <- c("Blueberry", "Cherry",
pie+     "Apple", "Boston Cream", "Other", "Vanilla Cream")

pie> pie(pie.sales) # default colours


pie> pie(pie.sales, col = c("purple", "violetred1", "green3",
pie+                        "cornsilk", "cyan", "white"))


pie> pie(pie.sales, col = gray(seq(0.4, 1.0, length.out = 6)))


pie> pie(pie.sales, density = 10, angle = 15 + 10 * 1:6)


pie> pie(pie.sales, clockwise = TRUE, main = "pie(*, clockwise = TRUE)")


pie> segments(0, 0, 0, 1, col = "red", lwd = 2)

pie> text(0, 1, "init.angle = 90", col = "red")

pie> n <- 200

pie> pie(rep(1, n), labels = "", col = rainbow(n), border = NA,
pie+     main = "pie(*, labels=\"\", col=rainbow(n), border=NA,..")


pie> ## Another case showing pie() is rather fun than science:
pie> ## (original by FinalBackwardsGlance on http://imgur.com/gallery/wWrpU4X)
pie> pie(c(Sky = 78, "Sunny side of pyramid" = 17, "Shady side of pyramid" = 5),
pie+     init.angle = 315, col = c("deepskyblue", "yellow", "yellow3"), border = FALSE)

pie(c(20, 80), init.angle=-40,
    col=c("white", "yellow"), 
    label=c("no pacman", "pacman"),
    border = "lightgrey")

… or switch from pie to waffle (seriously)

6.12 How to do 3D exploded pie charts

DON’T. And this time I mean it

sadly enough there would be packages for this, too

6.13 Extra: dynamite plots

a.k.a. Why is this bad?

age_by_group <- tapply(surveyrbioc$Q1,surveyrbioc$Q2,mean)
sd_by_group <- tapply(surveyrbioc$Q1,surveyrbioc$Q2,sd)
mybar <- barplot(age_by_group,col=c("khaki","salmon","firebrick"), ylim=c(0,max(age_by_group) + 5))
# mybar, inspect it
arrows(mybar, age_by_group,mybar, (age_by_group + sd_by_group), length = 0.15,angle= 90)

Dynamite plots VS boxplots

boxplot(Q1~Q2,
        data = surveyrbioc)

Median VS distribution VS actual points… What do you really want to show?

6.14 What can you do more with your plot?

  • change the points type - see type in ?plot
  • use log scales - see log
  • annotate (some of) the points - with text
  • change font sizes, styles and so on
  • use special characters with expression
  • save the plot
    • use the point-and-click interface in RStudio
    • code it

6.14.1 Saving your plots

General code structure for this

opendevice()
...
code for the plot
...
closedevice()
pdf("myfilename.pdf")
# see also alternatives:
## png()
## jpeg()
plot(mpg$displ,mpg$cty,
     col = mpg$mygroup)
dev.off()

6.15 Petals and sepals

6.16 Exercise session 5

Back to the iris. Three species are there. Explore the dataset in the following ways:

  • draw a histogram of the petal length. What do you see?

  • plot sepal length versus petal length. Add different colors to highlight the species

  • do the same for sepal width and sepal length, and this time use a different symbol for the species. Add a legend and a title if you want

  • (harder) calculate the mean values of each feature for each species, organizing it in a matrix where the rows are the species names. Generate a stacked bar plot with it, and another one where the bars are arranged horizontally

  • feel free to go back to the survey data and explore it further!

6.16.1 Exercise Session 5 - Solutions

hist(iris$Petal.Length)

plot(iris$Sepal.Length,iris$Petal.Length)

plot(iris$Sepal.Length,iris$Petal.Length,col=iris$Species)

plot(iris$Sepal.Width,iris$Sepal.Length, pch = as.numeric(factor(iris$Species)))
legend("topright",legend = levels(factor(iris$Species)),pch=unique(factor(iris$Species)))


sl_means <- tapply(iris$Sepal.Length,iris$Species,mean)
pl_means <- tapply(iris$Petal.Length,iris$Species,mean)
sw_means <- tapply(iris$Sepal.Width,iris$Species,mean)
pw_means <- tapply(iris$Petal.Width,iris$Species,mean)
mymat <- cbind(sl_means,pl_means,sw_means,pw_means)
barplot(mymat,legend.text = unique(iris$Species))

barplot(mymat,beside = TRUE,legend.text = unique(iris$Species))

6.17 Something cool to have an overview…

pairs(iris[,1:4],col=iris$Species)

You can use the panels even more cleverly, check the help of pairs!

This is a collection on graphs in R - with the underlying code too.

http://shiny.stat.ubc.ca/r-graph-catalog/

6.18 The gapminder project

https://www.youtube.com/watch?v=hVimVzgtD6w

6.19 Meet ggplot2

But first, meet the gapminder data

library(gapminder)
head(gapminder)
# A tibble: 6 × 6
  country     continent  year lifeExp      pop gdpPercap
  <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
1 Afghanistan Asia       1952    28.8  8425333      779.
2 Afghanistan Asia       1957    30.3  9240934      821.
3 Afghanistan Asia       1962    32.0 10267083      853.
4 Afghanistan Asia       1967    34.0 11537966      836.
5 Afghanistan Asia       1972    36.1 13079460      740.
6 Afghanistan Asia       1977    38.4 14880372      786.
head(country_colors)
         Nigeria            Egypt         Ethiopia Congo, Dem. Rep.     South Africa 
       "#7F3B08"        "#833D07"        "#873F07"        "#8B4107"        "#8F4407" 
           Sudan 
       "#934607" 
head(continent_colors)
   Africa  Americas      Asia    Europe   Oceania 
"#7F3B08" "#A50026" "#40004B" "#276419" "#313695" 

Variables:

  • country
  • continent
  • year
  • lifeExp, life expectancy at birth
  • pop, total population
  • gdpPercap, per-capita GDP

6.20 The ggplot2 philosophy

gg stands for the grammar of graphics

  • you provide the data
  • you map the data to aesthetics (shape, size, colour)
  • you add geoms to specify how you want to have the data plotted
  • you can have statistical transformations
  • facets allow you to do quick elegant multi plots

It can come across somewhat harder since

  • data need to be tidy - one observation per row
  • requires an extra step for abstraction

yet, it makes the whole process of “thinking data” more natural.

6.20.1 A quick dive into the many options

ggplot(gapminder,aes(x = gdpPercap, y = lifeExp))

ggplot(gapminder,aes(x = gdpPercap, y = lifeExp)) + geom_point()

We can store ggplot plot objects into a variable - and build upon that later

p <- ggplot(gapminder,aes(x = gdpPercap, y = lifeExp)) 
p + geom_point()

p + geom_point() + scale_x_log10()

p <- p + scale_x_log10()
p + geom_point(color="blue")

p + geom_point(color="steelblue", pch=19, size=8, alpha=1/4)

p + geom_point(aes(color=continent))

p + geom_point(aes(col=continent), size=4)

p + geom_point(aes(col=continent, size=pop)) 

p + geom_point(aes(col=continent, size=pop)) + geom_smooth()

niceone <- p + geom_point(aes(col=continent, size=pop)) + 
  geom_smooth(aes(col=continent),se=FALSE)
niceone

p + geom_point(aes(col=continent, size=pop)) + 
  geom_smooth(lwd=2, se=FALSE, method="lm", col="red")

p + geom_point(aes(col=continent, size=pop)) + 
  geom_smooth(aes(col=continent),lwd=2, se=FALSE, method="lm")

p + geom_point() + facet_wrap(~continent) 

p + geom_point(aes(col=continent)) + facet_wrap(~continent) 

p + geom_point(aes(col=continent)) + geom_smooth() + facet_wrap(~continent)

6.20.2 Saving the plots

ggsave(file="myplot.png")

6.20.3 Line plots

ggplot(gapminder,
       aes(x = year, y = lifeExp, group = country, color = country)
       ) +
  geom_line(lwd = 1, show.legend = FALSE) +
  scale_color_manual(values = country_colors) +
  theme_bw() + theme(strip.text = element_text(size = rel(1.1)))

bp <- ggplot(gapminder,
       aes(x = year, y = lifeExp, group = country, color = country)
       ) +
  geom_line(lwd = 1, show.legend = FALSE) + facet_wrap(~ continent) +
  scale_color_manual(values = country_colors) + theme(strip.text = element_text(size = rel(1.1)))
bp

plotly::ggplotly(bp)

6.20.4 Boxplots

# now it is a categorical x VS continuous y
p <- ggplot(gapminder, aes(x = continent, y = lifeExp)) 
p + geom_point()

p + geom_point(alpha=1/4)

It is so easy to escape dynamite plots!

p + geom_jitter()

p + geom_jitter(aes(col=continent))

p + geom_boxplot()

p + geom_boxplot() + geom_jitter(alpha=1/2)

6.20.5 Histograms

p <- ggplot(gapminder, aes(lifeExp))
p + geom_histogram()

p + geom_histogram(binwidth=1)

Stacked histogram are much easier in this framework

p + geom_histogram(aes(color=continent))

p + geom_histogram(aes(fill=continent))

p + geom_histogram(aes(fill=continent), position="identity")

… and so is the superimposing of more than one distribution

p + geom_histogram(aes(fill=continent), position="identity", alpha = 0.4)

Similar to histogram, you can use also density plots

p + geom_density(aes(fill=continent), alpha=1/4)

6.20.6 Themes: a quick way to put a new shirt on

niceone + theme_bw()

niceone + theme_void()

If you really really really have to…

library("ggthemes")
niceone + theme_excel() + scale_color_excel()

6.21 Exercise session 6 - Homework if you want

  • try to recreate the plots you did with base graphics, this time using ggplot2

  • pick a nice plot you would like to have in your next manuscript: can you think of what you need to do it? I am talking of

    • what data type?
    • what transformations?
    • what plot type/layer?

7 Step 5: SummarizedExperiment: your best friend for “bioinformatics datasets”

7.1 Next steps

Data in bioinformatics is often complex. To deal with this, developers define specialised data containers (termed classes) that match the properties of the data they need to handle.

This aspect is central to the Bioconductor(https://www.bioconductor.org) project which uses the same core data infrastructure across packages. This certainly contributed to Bioconductor’s success. Bioconductor package developers are advised to make use of existing infrastructure to provide coherence, interoperability and stability to the project as a whole.

To illustrate such an omics data container, we’ll present the SummarizedExperiment class.

7.2 SummarizedExperiment

The figure below represents the anatomy of SummarizedExperiment.

Objects of the class SummarizedExperiment contain :

  • One (or more) assay(s) containing the quantitative omics data (expression data), stored as a matrix-like object. Features (genes, transcripts, proteins, …) are defined along the rows and samples along the columns.

  • A sample metadata slot containing sample co-variates, stored as a data frame. Rows from this table represent samples (rows match exactly the columns of the expression data).

  • A feature metadata slot containing feature co-variates, stored as data frame. The rows of this dataframe’s match exactly the rows of the expression data.

The coordinated nature of the SummarizedExperiment guarantees that during data manipulation, the dimensions of the different slots will always match (i.e the columns in the expression data and then rows in the sample metadata, as well as the rows in the expression data and feature metadata) during data manipulation. For example, if we had to exclude one sample from the assay, it would be automatically removed from the sample metadata in the same operation.

The metadata slots can grow additional co-variates (columns) without affecting the other structures.

Questions
Q1 - Can you think of data examples what can fit into this container?
Q2 - What if the data has some “specific” peculiarities on top of this tabular-like representation?

7.2.1 Creating a SummarizedExperiment

rna <- read_csv("data/rnaseq.csv")
head(rna)
# A tibble: 6 × 19
  gene   sample expression organism   age sex   infection strain  time tissue mouse ENTREZID product
  <chr>  <chr>       <dbl> <chr>    <dbl> <chr> <chr>     <chr>  <dbl> <chr>  <dbl>    <dbl> <chr>  
1 Asl    GSM25…       1170 Mus mus…     8 Fema… Influenz… C57BL…     8 Cereb…    14   109900 argini…
2 Apod   GSM25…      36194 Mus mus…     8 Fema… Influenz… C57BL…     8 Cereb…    14    11815 apolip…
3 Cyp2d… GSM25…       4060 Mus mus…     8 Fema… Influenz… C57BL…     8 Cereb…    14    56448 cytoch…
4 Klk6   GSM25…        287 Mus mus…     8 Fema… Influenz… C57BL…     8 Cereb…    14    19144 kallik…
5 Fcrls  GSM25…         85 Mus mus…     8 Fema… Influenz… C57BL…     8 Cereb…    14    80891 Fc rec…
6 Slc2a4 GSM25…        782 Mus mus…     8 Fema… Influenz… C57BL…     8 Cereb…    14    20528 solute…
# ℹ 6 more variables: ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,
#   gene_biotype <chr>, phenotype_description <chr>, hsapiens_homolog_associated_gene_name <chr>
head(as.data.frame(rna))
     gene     sample expression     organism age    sex  infection  strain time     tissue mouse
1     Asl GSM2545336       1170 Mus musculus   8 Female InfluenzaA C57BL/6    8 Cerebellum    14
2    Apod GSM2545336      36194 Mus musculus   8 Female InfluenzaA C57BL/6    8 Cerebellum    14
3 Cyp2d22 GSM2545336       4060 Mus musculus   8 Female InfluenzaA C57BL/6    8 Cerebellum    14
4    Klk6 GSM2545336        287 Mus musculus   8 Female InfluenzaA C57BL/6    8 Cerebellum    14
5   Fcrls GSM2545336         85 Mus musculus   8 Female InfluenzaA C57BL/6    8 Cerebellum    14
6  Slc2a4 GSM2545336        782 Mus musculus   8 Female InfluenzaA C57BL/6    8 Cerebellum    14
  ENTREZID                                                                      product
1   109900                               argininosuccinate lyase, transcript variant X1
2    11815                                       apolipoprotein D, transcript variant 3
3    56448 cytochrome P450, family 2, subfamily d, polypeptide 22, transcript variant 2
4    19144                         kallikrein related-peptidase 6, transcript variant 2
5    80891                Fc receptor-like S, scavenger receptor, transcript variant X1
6    20528          solute carrier family 2 (facilitated glucose transporter), member 4
     ensembl_gene_id external_synonym chromosome_name   gene_biotype
1 ENSMUSG00000025533    2510006M18Rik               5 protein_coding
2 ENSMUSG00000022548             <NA>              16 protein_coding
3 ENSMUSG00000061740             2D22              15 protein_coding
4 ENSMUSG00000050063             Bssp               7 protein_coding
5 ENSMUSG00000015852    2810439C17Rik               3 protein_coding
6 ENSMUSG00000018566           Glut-4              11 protein_coding
                            phenotype_description hsapiens_homolog_associated_gene_name
1           abnormal circulating amino acid level                                   ASL
2                      abnormal lipid homeostasis                                  APOD
3                        abnormal skin morphology                                CYP2D6
4                         abnormal cytokine level                                  KLK6
5 decreased CD8-positive alpha-beta T cell number                                 FCRL2
6              abnormal circulating glucose level                                SLC2A4

Remember the rna dataset that we have used previously.

From this table we have already created 3 different tables - we read them in as serialized r objects.

count_matrix <- readRDS("data/count_matrix.RDS")
sample_metadata <- readRDS("data/sample_metadata.RDS")
gene_metadata <- readRDS("data/gene_metadata.RDS")
  • An expression matrix
count_matrix[1:5, ]
        GSM2545336 GSM2545337 GSM2545338 GSM2545339 GSM2545340 GSM2545341 GSM2545342 GSM2545343
Asl           1170        361        400        586        626        988        836        535
Apod         36194      10347       9173      10620      13021      29594      24959      13668
Cyp2d22       4060       1616       1603       1901       2171       3349       3122       2008
Klk6           287        629        641        578        448        195        186       1101
Fcrls           85        233        244        237        180         38         68        375
        GSM2545344 GSM2545345 GSM2545346 GSM2545347 GSM2545348 GSM2545349 GSM2545350 GSM2545351
Asl            586        597        938       1035        494        481        666        937
Apod         13230      15868      27769      34301      11258      11812      15816      29242
Cyp2d22       2254       2277       2985       3452       1883       2014       2417       3678
Klk6           537        567        327        233        742        881        828        250
Fcrls          199        177         89         67        300        233        231         81
        GSM2545352 GSM2545353 GSM2545354 GSM2545362 GSM2545363 GSM2545380
Asl            803        541        473        748        576       1192
Apod         20415      13682      11088      15916      11166      38148
Cyp2d22       2920       2216       1821       2842       2011       4019
Klk6           798        710        894        501        598        259
Fcrls          303        285        248        179        184         68
dim(count_matrix)
[1] 1474   22
  • A table describing the samples
sample_metadata
# A tibble: 22 × 9
   sample     organism       age sex    infection   strain   time tissue     mouse
   <chr>      <chr>        <dbl> <chr>  <chr>       <chr>   <dbl> <chr>      <dbl>
 1 GSM2545336 Mus musculus     8 Female InfluenzaA  C57BL/6     8 Cerebellum    14
 2 GSM2545337 Mus musculus     8 Female NonInfected C57BL/6     0 Cerebellum     9
 3 GSM2545338 Mus musculus     8 Female NonInfected C57BL/6     0 Cerebellum    10
 4 GSM2545339 Mus musculus     8 Female InfluenzaA  C57BL/6     4 Cerebellum    15
 5 GSM2545340 Mus musculus     8 Male   InfluenzaA  C57BL/6     4 Cerebellum    18
 6 GSM2545341 Mus musculus     8 Male   InfluenzaA  C57BL/6     8 Cerebellum     6
 7 GSM2545342 Mus musculus     8 Female InfluenzaA  C57BL/6     8 Cerebellum     5
 8 GSM2545343 Mus musculus     8 Male   NonInfected C57BL/6     0 Cerebellum    11
 9 GSM2545344 Mus musculus     8 Female InfluenzaA  C57BL/6     4 Cerebellum    22
10 GSM2545345 Mus musculus     8 Male   InfluenzaA  C57BL/6     4 Cerebellum    13
# ℹ 12 more rows
  • A table describing the genes
gene_metadata
# A tibble: 1,474 × 9
   gene    ENTREZID product            ensembl_gene_id external_synonym chromosome_name gene_biotype
   <chr>      <dbl> <chr>              <chr>           <chr>            <chr>           <chr>       
 1 Asl       109900 argininosuccinate… ENSMUSG0000002… 2510006M18Rik    5               protein_cod…
 2 Apod       11815 apolipoprotein D,… ENSMUSG0000002… <NA>             16              protein_cod…
 3 Cyp2d22    56448 cytochrome P450, … ENSMUSG0000006… 2D22             15              protein_cod…
 4 Klk6       19144 kallikrein relate… ENSMUSG0000005… Bssp             7               protein_cod…
 5 Fcrls      80891 Fc receptor-like … ENSMUSG0000001… 2810439C17Rik    3               protein_cod…
 6 Slc2a4     20528 solute carrier fa… ENSMUSG0000001… Glut-4           11              protein_cod…
 7 Exd2       97827 exonuclease 3'-5'… ENSMUSG0000003… 4930539P14Rik    12              protein_cod…
 8 Gjc2      118454 gap junction prot… ENSMUSG0000004… B230382L12Rik    11              protein_cod…
 9 Plp1       18823 proteolipid prote… ENSMUSG0000003… DM20             X               protein_cod…
10 Gnb4       14696 guanine nucleotid… ENSMUSG0000002… 6720453A21Rik    3               protein_cod…
# ℹ 1,464 more rows
# ℹ 2 more variables: phenotype_description <chr>, hsapiens_homolog_associated_gene_name <chr>

We will create a SummarizedExperiment from these tables:

  • The count matrix that will be used as the assay

  • The table describing the samples will be used as the sample metadata slot

  • The table describing the genes will be used as the features metadata slot

To do this we can put the different parts together using the SummarizedExperiment constructor:

#BiocManager::install("SummarizedExperiment")
library("SummarizedExperiment")
se <- SummarizedExperiment(assays = count_matrix,
                           colData = sample_metadata,
                           rowData = gene_metadata)
se
class: SummarizedExperiment 
dim: 1474 22 
metadata(0):
assays(1): ''
rownames(1474): Asl Apod ... Lmx1a Pbx1
rowData names(9): gene ENTREZID ... phenotype_description
  hsapiens_homolog_associated_gene_name
colnames(22): GSM2545336 GSM2545337 ... GSM2545363 GSM2545380
colData names(9): sample organism ... tissue mouse

Using this data structure, we can access the expression matrix with the assay function:

head(assay(se))
        GSM2545336 GSM2545337 GSM2545338 GSM2545339 GSM2545340 GSM2545341 GSM2545342 GSM2545343
Asl           1170        361        400        586        626        988        836        535
Apod         36194      10347       9173      10620      13021      29594      24959      13668
Cyp2d22       4060       1616       1603       1901       2171       3349       3122       2008
Klk6           287        629        641        578        448        195        186       1101
Fcrls           85        233        244        237        180         38         68        375
Slc2a4         782        231        248        265        313        786        528        249
        GSM2545344 GSM2545345 GSM2545346 GSM2545347 GSM2545348 GSM2545349 GSM2545350 GSM2545351
Asl            586        597        938       1035        494        481        666        937
Apod         13230      15868      27769      34301      11258      11812      15816      29242
Cyp2d22       2254       2277       2985       3452       1883       2014       2417       3678
Klk6           537        567        327        233        742        881        828        250
Fcrls          199        177         89         67        300        233        231         81
Slc2a4         266        357        654        693        271        304        349        715
        GSM2545352 GSM2545353 GSM2545354 GSM2545362 GSM2545363 GSM2545380
Asl            803        541        473        748        576       1192
Apod         20415      13682      11088      15916      11166      38148
Cyp2d22       2920       2216       1821       2842       2011       4019
Klk6           798        710        894        501        598        259
Fcrls          303        285        248        179        184         68
Slc2a4         513        320        248        350        317        796
dim(assay(se))
[1] 1474   22

We can access the sample metadata using the colData function:

colData(se)
DataFrame with 22 rows and 9 columns
                sample     organism       age         sex   infection      strain      time
           <character>  <character> <numeric> <character> <character> <character> <numeric>
GSM2545336  GSM2545336 Mus musculus         8      Female  InfluenzaA     C57BL/6         8
GSM2545337  GSM2545337 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545338  GSM2545338 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545339  GSM2545339 Mus musculus         8      Female  InfluenzaA     C57BL/6         4
GSM2545340  GSM2545340 Mus musculus         8        Male  InfluenzaA     C57BL/6         4
...                ...          ...       ...         ...         ...         ...       ...
GSM2545353  GSM2545353 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545354  GSM2545354 Mus musculus         8        Male NonInfected     C57BL/6         0
GSM2545362  GSM2545362 Mus musculus         8      Female  InfluenzaA     C57BL/6         4
GSM2545363  GSM2545363 Mus musculus         8        Male  InfluenzaA     C57BL/6         4
GSM2545380  GSM2545380 Mus musculus         8      Female  InfluenzaA     C57BL/6         8
                tissue     mouse
           <character> <numeric>
GSM2545336  Cerebellum        14
GSM2545337  Cerebellum         9
GSM2545338  Cerebellum        10
GSM2545339  Cerebellum        15
GSM2545340  Cerebellum        18
...                ...       ...
GSM2545353  Cerebellum         4
GSM2545354  Cerebellum         2
GSM2545362  Cerebellum        20
GSM2545363  Cerebellum        12
GSM2545380  Cerebellum        19
dim(colData(se))
[1] 22  9

We can also access the feature metadata using the rowData function:

head(rowData(se))
DataFrame with 6 rows and 9 columns
               gene  ENTREZID                product    ensembl_gene_id external_synonym
        <character> <numeric>            <character>        <character>      <character>
Asl             Asl    109900 argininosuccinate ly.. ENSMUSG00000025533    2510006M18Rik
Apod           Apod     11815 apolipoprotein D, tr.. ENSMUSG00000022548               NA
Cyp2d22     Cyp2d22     56448 cytochrome P450, fam.. ENSMUSG00000061740             2D22
Klk6           Klk6     19144 kallikrein related-p.. ENSMUSG00000050063             Bssp
Fcrls         Fcrls     80891 Fc receptor-like S, .. ENSMUSG00000015852    2810439C17Rik
Slc2a4       Slc2a4     20528 solute carrier famil.. ENSMUSG00000018566           Glut-4
        chromosome_name   gene_biotype  phenotype_description hsapiens_homolog_associated_gene_name
            <character>    <character>            <character>                           <character>
Asl                   5 protein_coding abnormal circulating..                                   ASL
Apod                 16 protein_coding abnormal lipid homeo..                                  APOD
Cyp2d22              15 protein_coding abnormal skin morpho..                                CYP2D6
Klk6                  7 protein_coding abnormal cytokine le..                                  KLK6
Fcrls                 3 protein_coding decreased CD8-positi..                                 FCRL2
Slc2a4               11 protein_coding abnormal circulating..                                SLC2A4
dim(rowData(se))
[1] 1474    9

7.2.2 Subsetting a SummarizedExperiment

SummarizedExperiment can be subset just like with data frames, with numerics or with characters of logicals.

Below, we create a new instance of class SummarizedExperiment that contains only the 5 first features for the 3 first samples.

se1 <- se[1:5, 1:3]
se1
class: SummarizedExperiment 
dim: 5 3 
metadata(0):
assays(1): ''
rownames(5): Asl Apod Cyp2d22 Klk6 Fcrls
rowData names(9): gene ENTREZID ... phenotype_description
  hsapiens_homolog_associated_gene_name
colnames(3): GSM2545336 GSM2545337 GSM2545338
colData names(9): sample organism ... tissue mouse
colData(se1)
DataFrame with 3 rows and 9 columns
                sample     organism       age         sex   infection      strain      time
           <character>  <character> <numeric> <character> <character> <character> <numeric>
GSM2545336  GSM2545336 Mus musculus         8      Female  InfluenzaA     C57BL/6         8
GSM2545337  GSM2545337 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545338  GSM2545338 Mus musculus         8      Female NonInfected     C57BL/6         0
                tissue     mouse
           <character> <numeric>
GSM2545336  Cerebellum        14
GSM2545337  Cerebellum         9
GSM2545338  Cerebellum        10
rowData(se1)
DataFrame with 5 rows and 9 columns
               gene  ENTREZID                product    ensembl_gene_id external_synonym
        <character> <numeric>            <character>        <character>      <character>
Asl             Asl    109900 argininosuccinate ly.. ENSMUSG00000025533    2510006M18Rik
Apod           Apod     11815 apolipoprotein D, tr.. ENSMUSG00000022548               NA
Cyp2d22     Cyp2d22     56448 cytochrome P450, fam.. ENSMUSG00000061740             2D22
Klk6           Klk6     19144 kallikrein related-p.. ENSMUSG00000050063             Bssp
Fcrls         Fcrls     80891 Fc receptor-like S, .. ENSMUSG00000015852    2810439C17Rik
        chromosome_name   gene_biotype  phenotype_description hsapiens_homolog_associated_gene_name
            <character>    <character>            <character>                           <character>
Asl                   5 protein_coding abnormal circulating..                                   ASL
Apod                 16 protein_coding abnormal lipid homeo..                                  APOD
Cyp2d22              15 protein_coding abnormal skin morpho..                                CYP2D6
Klk6                  7 protein_coding abnormal cytokine le..                                  KLK6
Fcrls                 3 protein_coding decreased CD8-positi..                                 FCRL2

We can also use the colData() function to subset on something from the sample metadata, or the rowData() to subset on something from the feature metadata. For example, here we keep only miRNAs and the non infected samples:

se1 <- se[rowData(se)$gene_biotype == "miRNA",
          colData(se)$infection == "NonInfected"]
se1
class: SummarizedExperiment 
dim: 7 7 
metadata(0):
assays(1): ''
rownames(7): Mir1901 Mir378a ... Mir128-1 Mir7682
rowData names(9): gene ENTREZID ... phenotype_description
  hsapiens_homolog_associated_gene_name
colnames(7): GSM2545337 GSM2545338 ... GSM2545353 GSM2545354
colData names(9): sample organism ... tissue mouse
assay(se1)
         GSM2545337 GSM2545338 GSM2545343 GSM2545348 GSM2545349 GSM2545353 GSM2545354
Mir1901          45         44         74         55         68         33         60
Mir378a          11          7          9          4         12          4          8
Mir133b           4          6          5          4          6          7          3
Mir30c-2         10          6         16         12          8         17         15
Mir149            1          2          0          0          0          0          2
Mir128-1          4          1          2          2          1          2          1
Mir7682           2          0          4          1          3          5          5
colData(se1)
DataFrame with 7 rows and 9 columns
                sample     organism       age         sex   infection      strain      time
           <character>  <character> <numeric> <character> <character> <character> <numeric>
GSM2545337  GSM2545337 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545338  GSM2545338 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545343  GSM2545343 Mus musculus         8        Male NonInfected     C57BL/6         0
GSM2545348  GSM2545348 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545349  GSM2545349 Mus musculus         8        Male NonInfected     C57BL/6         0
GSM2545353  GSM2545353 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545354  GSM2545354 Mus musculus         8        Male NonInfected     C57BL/6         0
                tissue     mouse
           <character> <numeric>
GSM2545337  Cerebellum         9
GSM2545338  Cerebellum        10
GSM2545343  Cerebellum        11
GSM2545348  Cerebellum         8
GSM2545349  Cerebellum         7
GSM2545353  Cerebellum         4
GSM2545354  Cerebellum         2
rowData(se1)
DataFrame with 7 rows and 9 columns
                gene  ENTREZID        product    ensembl_gene_id external_synonym chromosome_name
         <character> <numeric>    <character>        <character>      <character>     <character>
Mir1901      Mir1901 100316686  microRNA 1901 ENSMUSG00000084565         Mirn1901              18
Mir378a      Mir378a    723889  microRNA 378a ENSMUSG00000105200          Mirn378              18
Mir133b      Mir133b    723817  microRNA 133b ENSMUSG00000065480         mir 133b               1
Mir30c-2    Mir30c-2    723964 microRNA 30c-2 ENSMUSG00000065567        mir 30c-2               1
Mir149        Mir149    387167   microRNA 149 ENSMUSG00000065470          Mirn149               1
Mir128-1    Mir128-1    387147 microRNA 128-1 ENSMUSG00000065520          Mirn128               1
Mir7682      Mir7682 102466847  microRNA 7682 ENSMUSG00000106406     mmu-mir-7682               1
         gene_biotype  phenotype_description hsapiens_homolog_associated_gene_name
          <character>            <character>                           <character>
Mir1901         miRNA                     NA                                    NA
Mir378a         miRNA abnormal mitochondri..                               MIR378A
Mir133b         miRNA no abnormal phenotyp..                               MIR133B
Mir30c-2        miRNA                     NA                               MIR30C2
Mir149          miRNA increased circulatin..                                    NA
Mir128-1        miRNA no abnormal phenotyp..                              MIR128-1
Mir7682         miRNA                     NA                                    NA

For the following exercise, you should download the SE.rda object (that contains the se object), and open the file using the ‘load()’ function.

download.file(url = "https://raw.githubusercontent.com/UCLouvain-CBIO/bioinfo-training-01-intro-r/master/data/SE.rda",
              destfile = "data/SE.rda")
load(file = "data/SE.rda")

7.3 Exercise session 6

Extract the gene expression levels of the 3 first genes in samples at time 0 and at time 8.

7.3.1 Exercise session 6 - Solutions

assay(se)[1:3, colData(se)$time != 4]
        GSM2545336 GSM2545337 GSM2545338 GSM2545341 GSM2545342 GSM2545343 GSM2545346 GSM2545347
Asl           1170        361        400        988        836        535        938       1035
Apod         36194      10347       9173      29594      24959      13668      27769      34301
Cyp2d22       4060       1616       1603       3349       3122       2008       2985       3452
        GSM2545348 GSM2545349 GSM2545351 GSM2545353 GSM2545354 GSM2545380
Asl            494        481        937        541        473       1192
Apod         11258      11812      29242      13682      11088      38148
Cyp2d22       1883       2014       3678       2216       1821       4019

# Equivalent to
assay(se)[1:3, colData(se)$time == 0 | colData(se)$time == 8]
        GSM2545336 GSM2545337 GSM2545338 GSM2545341 GSM2545342 GSM2545343 GSM2545346 GSM2545347
Asl           1170        361        400        988        836        535        938       1035
Apod         36194      10347       9173      29594      24959      13668      27769      34301
Cyp2d22       4060       1616       1603       3349       3122       2008       2985       3452
        GSM2545348 GSM2545349 GSM2545351 GSM2545353 GSM2545354 GSM2545380
Asl            494        481        937        541        473       1192
Apod         11258      11812      29242      13682      11088      38148
Cyp2d22       1883       2014       3678       2216       1821       4019

7.3.1.1 Adding variables to metadata

We can also add information to the metadata. Suppose that you want to add the center where the samples were collected…

colData(se)$center <- rep("University of Illinois", nrow(colData(se)))
colData(se)
DataFrame with 22 rows and 10 columns
                sample     organism       age         sex   infection      strain      time
           <character>  <character> <numeric> <character> <character> <character> <numeric>
GSM2545336  GSM2545336 Mus musculus         8      Female  InfluenzaA     C57BL/6         8
GSM2545337  GSM2545337 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545338  GSM2545338 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545339  GSM2545339 Mus musculus         8      Female  InfluenzaA     C57BL/6         4
GSM2545340  GSM2545340 Mus musculus         8        Male  InfluenzaA     C57BL/6         4
...                ...          ...       ...         ...         ...         ...       ...
GSM2545353  GSM2545353 Mus musculus         8      Female NonInfected     C57BL/6         0
GSM2545354  GSM2545354 Mus musculus         8        Male NonInfected     C57BL/6         0
GSM2545362  GSM2545362 Mus musculus         8      Female  InfluenzaA     C57BL/6         4
GSM2545363  GSM2545363 Mus musculus         8        Male  InfluenzaA     C57BL/6         4
GSM2545380  GSM2545380 Mus musculus         8      Female  InfluenzaA     C57BL/6         8
                tissue     mouse                 center
           <character> <numeric>            <character>
GSM2545336  Cerebellum        14 University of Illinois
GSM2545337  Cerebellum         9 University of Illinois
GSM2545338  Cerebellum        10 University of Illinois
GSM2545339  Cerebellum        15 University of Illinois
GSM2545340  Cerebellum        18 University of Illinois
...                ...       ...                    ...
GSM2545353  Cerebellum         4 University of Illinois
GSM2545354  Cerebellum         2 University of Illinois
GSM2545362  Cerebellum        20 University of Illinois
GSM2545363  Cerebellum        12 University of Illinois
GSM2545380  Cerebellum        19 University of Illinois

This illustrates that the metadata slots can grow indefinitely without affecting the other structures!

Take-home message

  • SummarizedExperiment represent an efficient way to store and to handle omics data.

  • They are used in many Bioconductor packages.

If you follow next training focused on RNA sequencing analysis, you will learn to use the Bioconductor DESeq2 package to do some differential expression analyses. DESeq2’s whole analysis is handled in a SummarizedExperiment.

8 Useful material

Books

Courses

Misc

Session Info

sessionInfo()
R version 4.3.0 (2023-04-21)
Platform: x86_64-apple-darwin20 (64-bit)
Running under: macOS Monterey 12.6.4

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Europe/Berlin
tzcode source: internal

attached base packages:
[1] stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] clusterProfiler_4.8.1       topGO_2.52.0                SparseM_1.81               
 [4] GO.db_3.17.0                graph_1.78.0                GeneTonic_2.4.0            
 [7] iSEEu_1.12.0                iSEEhex_1.2.0               iSEE_2.12.0                
[10] SingleCellExperiment_1.22.0 pheatmap_1.0.12             apeglm_1.22.1              
[13] pcaExplorer_2.26.1          bigmemory_4.6.1             edgeR_3.42.4               
[16] limma_3.56.2                ExploreModelMatrix_1.12.0   GenomicFeatures_1.52.1     
[19] org.Hs.eg.db_3.17.0         AnnotationDbi_1.62.1        DESeq2_1.40.2              
[22] tximeta_1.18.0              macrophage_1.16.0           knitr_1.43                 
[25] BiocStyle_2.28.0            SummarizedExperiment_1.30.2 Biobase_2.60.0             
[28] GenomicRanges_1.52.0        GenomeInfoDb_1.36.1         IRanges_2.34.1             
[31] S4Vectors_0.38.1            BiocGenerics_0.46.0         MatrixGenerics_1.12.2      
[34] matrixStats_1.0.0           lubridate_1.9.2             forcats_1.0.0              
[37] stringr_1.5.0               dplyr_1.1.2                 purrr_1.0.1                
[40] readr_2.1.4                 tidyr_1.3.0                 tibble_3.2.1               
[43] tidyverse_2.0.0             ggthemes_4.2.4              gapminder_1.0.0            
[46] ggplot2_3.4.2               MASS_7.3-60                 rmarkdown_2.22             

loaded via a namespace (and not attached):
  [1] GSEABase_1.62.0               vroom_1.6.3                   progress_1.2.2               
  [4] DT_0.28                       Biostrings_2.68.1             pagedown_0.20                
  [7] vctrs_0.6.3                   digest_0.6.32                 png_0.1-8                    
 [10] shape_1.4.6                   shinyBS_0.61.1                registry_0.5-1               
 [13] ggrepel_0.9.3                 reshape2_1.4.4                qvalue_2.32.0                
 [16] httpuv_1.6.11                 foreach_1.5.2                 withr_2.5.0                  
 [19] ggfun_0.1.1                   xfun_0.39                     ellipsis_0.3.2               
 [22] survival_3.5-5                memoise_2.0.1                 xaringanExtra_0.7.0          
 [25] hexbin_1.28.3                 gson_0.1.0                    tidytree_0.4.2               
 [28] GlobalOptions_0.1.2           prettyunits_1.1.1             KEGGREST_1.40.0              
 [31] promises_1.2.0.1              httr_1.4.6                    downloader_0.4               
 [34] restfulr_0.0.15               ps_1.7.5                      rstudioapi_0.14              
 [37] shinyAce_0.4.2                DOSE_3.26.1                   miniUI_0.1.1.1               
 [40] generics_0.1.3                archive_1.1.5                 base64enc_0.1-3              
 [43] processx_3.8.1                curl_5.0.1                    zlibbioc_1.46.0              
 [46] ggraph_2.1.0                  polyclip_1.10-4               ca_0.71.1                    
 [49] GenomeInfoDbData_1.2.10       RBGL_1.76.0                   threejs_0.3.3                
 [52] interactiveDisplayBase_1.38.0 xtable_1.8-4                  doParallel_1.0.17            
 [55] evaluate_0.21                 S4Arrays_1.0.4                BiocFileCache_2.8.0          
 [58] hms_1.1.3                     bookdown_0.34                 colorspace_2.1-0             
 [61] filelock_1.0.2                visNetwork_2.1.2              shinyWidgets_0.7.6           
 [64] magrittr_2.0.3                Rgraphviz_2.44.0              ggtree_3.8.0                 
 [67] later_1.3.1                   viridis_0.6.3                 lattice_0.21-8               
 [70] NMF_0.26                      genefilter_1.82.1             shadowtext_0.1.2             
 [73] XML_3.99-0.14                 cowplot_1.1.1                 pillar_1.9.0                 
 [76] nlme_3.1-162                  iterators_1.0.14              gridBase_0.4-7               
 [79] compiler_4.3.0                stringi_1.7.12                shinycssloaders_1.0.0        
 [82] Category_2.66.0               TSP_1.2-4                     dendextend_1.17.1            
 [85] GenomicAlignments_1.36.0      plyr_1.8.8                    crayon_1.5.2                 
 [88] BiocIO_1.10.0                 gridGraphics_0.5-1            emdbook_1.3.12               
 [91] locfit_1.5-9.8                graphlayouts_1.0.0            bit_4.0.5                    
 [94] chromote_0.1.1                fastmatch_1.1-3               codetools_0.2-19             
 [97] crosstalk_1.2.0               bslib_0.5.0                   GetoptLong_1.0.5             
[100] plotly_4.10.2                 mime_0.12                     splines_4.3.0                
[103] circlize_0.4.15               Rcpp_1.0.10                   servr_0.27                   
[106] dbplyr_2.3.2                  tippy_0.1.0                   HDO.db_0.99.1                
[109] blob_1.2.4                    utf8_1.2.3                    clue_0.3-64                  
[112] BiocVersion_3.17.1            AnnotationFilter_1.24.0       fs_1.6.2                     
[115] backbone_2.1.2                expm_0.999-7                  ggplotify_0.1.1              
[118] Matrix_1.5-4.1                tzdb_0.4.0                    tweenr_2.0.2                 
[121] pkgconfig_2.0.3               tools_4.3.0                   cachem_1.0.8                 
[124] RSQLite_2.3.1                 viridisLite_0.4.2             DBI_1.1.3                    
[127] numDeriv_2016.8-1.1           fastmap_1.1.1                 scales_1.2.1                 
[130] grid_4.3.0                    shinydashboard_0.7.2          Rsamtools_2.16.0             
[133] AnnotationHub_3.8.0           sass_0.4.6                    patchwork_1.1.2              
[136] coda_0.19-4                   BiocManager_1.30.21           fontawesome_0.5.1            
[139] farver_2.1.1                  scatterpie_0.2.1              tidygraph_1.2.3              
[142] mgcv_1.8-42                   yaml_2.3.7                    renderthis_0.2.0             
[145] AnnotationForge_1.42.2        rtracklayer_1.60.0            cli_3.6.1                    
[148] webshot_0.5.5                 lifecycle_1.0.3               rsconnect_0.8.29             
[151] mvtnorm_1.2-2                 xaringan_0.28                 tximport_1.28.0              
[154] rintrojs_0.3.2                BiocParallel_1.34.2           annotate_1.78.0              
[157] timechange_0.2.0              gtable_0.3.3                  rjson_0.2.21                 
[160] ggridges_0.5.4                ape_5.7-1                     parallel_4.3.0               
[163] jsonlite_1.8.5                colourpicker_1.2.0            seriation_1.4.2              
[166] bitops_1.0-7                  bigmemory.sri_0.1.6           bit64_4.0.5                  
[169] assertthat_0.2.1              yulab.utils_0.0.6             heatmaply_1.4.2              
[172] bs4Dash_2.3.0                 bdsmatrix_1.3-6               icons_0.2.0                  
[175] jquerylib_0.1.4               highr_0.10                    GOSemSim_2.26.0              
[178] shinyjs_2.1.0                 lazyeval_0.2.2                shiny_1.7.4                  
[181] dynamicTreeCut_1.63-1         enrichplot_1.20.0             htmltools_0.5.5              
[184] rappdirs_0.3.3                formatR_1.14                  ensembldb_2.24.0             
[187] glue_1.6.2                    XVector_0.40.0                RCurl_1.98-1.12              
[190] treeio_1.24.1                 ComplexUpset_1.3.3            gridExtra_2.3                
[193] igraph_1.5.0                  R6_2.5.1                      labeling_0.4.2               
[196] cluster_2.1.4                 rngtools_1.5.2                bbmle_1.0.25                 
[199] aplot_0.1.10                  DelayedArray_0.26.3           tidyselect_1.2.0             
[202] vipor_0.4.5                   ProtGenerics_1.32.0           GOstats_2.66.0               
[205] ggforce_0.4.1                 xml2_1.3.4                    emo_0.0.0.9000               
[208] munsell_0.5.0                 data.table_1.14.8             websocket_1.4.1              
[211] fgsea_1.26.0                  htmlwidgets_1.6.2             ComplexHeatmap_2.16.0        
[214] RColorBrewer_1.1-3            biomaRt_2.56.1                rlang_1.1.1                  
[217] uuid_1.1-0                    fansi_1.0.4                  
LS0tCnRpdGxlOiA+CiAgSW50cm9kdWN0aW9uIHRvIFIgYW5kIEJpb2NvbmR1Y3RvciAtIGhhbmRzLW9uIHNlc3Npb24Kc3VidGl0bGU6ID4KICBNU0UgLSBNb2R1bGUgR2VuZXRpYyBFcGlkZW1pb2xvZ3kKICA8cCBhbGlnbj0iY2VudGVyIj4KICA8L3A+CiAgPGEgaHJlZj0iaHR0cHM6Ly93d3cudW5pbWVkaXppbi1tYWluei5kZS9pbWJlaS9zdGFydHNlaXRlL21zZS8iPjxpbWcgc3JjPSJpbWFnZXMvbXNlX2xvZ28uanBnIiBhbHQ9IiIgaGVpZ2h0PSIxMDAiLz48L2E+CmF1dGhvcjoKLSBuYW1lOiA8YSBocmVmPSJodHRwczovL2ZlZGVyaWNvbWFyaW5pLmdpdGh1Yi5pbyI+RmVkZXJpY28gTWFyaW5pIChtYXJpbmlmQHVuaS1tYWluei5kZSk8L2E+PGJyPjxhIGhyZWY9Imh0dHBzOi8vd3d3LnVuaW1lZGl6aW4tbWFpbnouZGUvaW1iZWkvIj5JTUJFSSwgVW5pdmVyc2l0eSBNZWRpY2FsIENlbnRlciBNYWluejwvYT48YnI+PGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9GZWRlQmlvaW5mbyI+YHIgaWNvbnM6OmZvbnRhd2Vzb21lKCd0d2l0dGVyJylgIGBARmVkZUJpb2luZm9gPC9hPgotIG5hbWU6IEFsaWNpYSBTY2h1bHplIChhbHBvcGxhd0B1bmktbWFpbnouZGUpPGJyPjxhIGhyZWY9Imh0dHBzOi8vd3d3LnVuaW1lZGl6aW4tbWFpbnouZGUvaW1iZWkvIj5JTUJFSSwgVW5pdmVyc2l0eSBNZWRpY2FsIENlbnRlciBNYWluejwvYT48YnI+CmRhdGU6ICIyMDIzLzA3LzA1LTA2IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogY29zbW8KICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBjb2xsYXBzZSA9IFRSVUUsCiAgY29tbWVudCA9ICIjIiwKICBlcnJvciA9IEZBTFNFLAogIHdhcm5pbmcgPSBGQUxTRSwKICBtZXNzYWdlID0gRkFMU0UKKQpvcHRpb25zKHdpZHRoID0gMTAwKQpgYGAKCgpUaGlzIGxlY3R1cmUgaXMgaW5zcGlyZWQgaW4gaXRzIHN0cnVjdHVyZSBhbmQgb3JnYW5pc2F0aW9uIGJ5IHRoZSAiSW50cm9kdWN0aW9uIHRvIGRhdGEgYW5hbHlzaXMgd2l0aCBSIGFuZCBCaW9jb25kdWN0b3IiIC0gaHR0cHM6Ly9jYXJwZW50cmllcy1pbmN1YmF0b3IuZ2l0aHViLmlvL2Jpb2MtaW50cm8vCgojIFN0ZXAgMDogUiBhbmQgUlN0dWRpbywga25vdyB5b3VyIHRvb2xzCgojIyBXaGF0IGlzIFI/IFdoeSBzaG91bGQgSSB1c2UgUj8gCgpBdmFpbGFibGUgYXQgYHd3dy5yLXByb2plY3Qub3JnYAoKLSAqZnJlZSogc3RhdGlzdGljYWwgZW52aXJvbm1lbnQgZm9yIGludGVyYWN0aXZlIHVzZQotIGludGVwcmV0ZWQsIGZ1bmN0aW9uYWwgc2NyaXB0aW5nL3Byb2dyYW1taW5nIGxhbmd1YWdlIC0geW91IHR5cGUgaW4sIHlvdSBzZWUgb3V0cHV0Ci0gZGVzY2VuZHMgZnJvbSB0aGUgUyBsYW5ndWFnZSwgd3JpdHRlbiBieSBzdGF0aXN0aWNpYW5zIGZvciBzdGF0aXN0aWNpYW5zCiAgCldoYXQgY2FuIHlvdSBkbyB3aXRoIFI/CgotIEFueXRoaW5nIQogIC0gZG8gY2FsY3VsYXRpb25zCiAgLSB3cml0ZSBmdW5jdGlvbnMKICAtIGFuYWx5c2UgZGF0YS4gQUxMIHRoZSBkYXRhLiBXZWxsLCBhbG1vc3QuIEJ1dCByZWFsbHksIGFsbW9zdC4KICAtIGFwcGx5IGFkdmFuY2VkIHN0YXRpc3RpY2FsIHRlY2huaXF1ZXMKICAtIGRvIGJlYXV0aWZ1bCAmIHB1YmxpY2F0aW9uLXJlYWR5IHBsb3RzCiAgLSBkZXZlbG9wIGludGVyYWN0aXZlIHdlYi1hcHBsaWNhdGlvbnMKICAtIHByZXNlbnRhdGlvbnMgJiBkb2N1bWVudHMgKHRoaXMgb25lKQoKV2h5IHNob3VsZCBJIHVzZSBSPwoKLSBpdCB3b3JrcyEgQW5kIGl0IGlzIHF1aXRlIHBvd2VyZnVsCi0gaXQgaXMgZnJlZSwgb3Blbi1zb3VyY2UsIGFuZCBhdmFpbGFibGUgZm9yIGFsbCBPUwotIHlvdSBjYW4gKnJlYWxseSogZG8gd2hhdGV2ZXIgeW91IG1pZ2h0IGFpbSB0byBkbyBpbiB0ZXJtcyBvZiBzdGF0aXN0aWNzCi0gaXQgb2ZmZXJzIGF3ZXNvbWUgcG9zc2liaWxpdGllcyBmb3IgKGludGVyYWN0aXZlKSBncmFwaGluZwotIGl0IGhhcyBhIHdpZGUsIGFjdGl2ZSBhbmQgY29tcGV0ZW50IGNvbW11bml0eSAob2ssIGNvbW11bml0aWVzOiBzdGF0aXN0aWNzLCBiaW9pbmZvcm1hdGljcywgbWFjaGluZSBsZWFybmluZywgLi4uKQotIGl0IGNhbiBiZSBleHRlbmRlZCB3aXRoIHBhY2thZ2VzLiBNb3JlIHBvd2VyIQotIGVzY2FwZSBQb2ludC1hbmQtQ2xpY2stbGFuZCwgeW91IHdvcmsgd2l0aCBzeW50YXg6IHlvdSBjYW4gdXNlLCByZS11c2UgZWxlbWVudHMsIHZhbGlkYXRlICYgcmVwcm9kdWNlIGFuYWx5c2lzCgpXaHkgc2hvdWxkIEkgKm5vdCogdXNlIFI/CgotIHlvdSB1c2UgaXQgb3IgbG9zZSBpdAotIHRoZSBsZWFybmluZyBjdXJ2ZSBtaWdodCBiZSBzdGVlcAotIGNhbiBiZSBmcnVzdHJhdGluZyBpZiB5b3UgaGF2ZSBlcnJvcnMKLSBoZWxwIG1pZ2h0IGJlIGF2YWlsYWJsZSwgYnV0IGl0IGlzIHZlcnkgdGVjaG5pY2FsCi0gbWFueSBwYWNrYWdlcywgYSBibGVzc2luZyBhbmQgYSBjdXJzZTogaG93IG1hbnksIGhvdyBnb29kIAoKIyMgTGV0J3MgZ2V0IHN0YXJ0ZWQhCgpHZXQgUiAtIGFuZCBSU3R1ZGlvCgotIGdvIHRvIGh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyBhbmQgZ2V0IHRoZSBsYXRlc3QgdmVyc2lvbgotIGdvIHRvIGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Byb2R1Y3RzL3JzdHVkaW8vIGFuZCBkb3dubG9hZCBSU3R1ZGlvCi0gLi4uIG9yIHVzZSB5b3VyIHRleHQgZWRpdG9yIG9mIGNob2ljZQoKQWx0ZXJuYXRpdmVzOiAKCi0gT3BlbkFuYWx5dGljcyBBcmNoaXRlY3QgKGh0dHBzOi8vd3d3LmdldGFyY2hpdGVjdC5pby8pCi0gTWljcm9zb2Z0IFIgVG9vbHMgZm9yIFZpc3VhbCBTdHVkaW8gKHd3dy52aXN1YWxzdHVkaW8uY29tL3ZzL3J0dnMvKQotIEVtYWNzIFNwZWFrcyBTdGF0aXN0aWNzLi4uCgoKIyMgRmlyc3QgcmlkZTogbG9vayBhcm91bmQgeW91CgpPcGVuIHVwIFJTdHVkaW8gLSBZb3UnbGwgaGF2ZSBmb3VyIHBhbmVzCgotIHRoZSBTb3VyY2UgZm9yIHlvdXIgc2NyaXB0cyBhbmQgZG9jdW1lbnRzICh0b3AtbGVmdCwgaW4gdGhlIGRlZmF1bHQgbGF5b3V0KQotIHlvdXIgRW52aXJvbm1lbnQvSGlzdG9yeSAodG9wLXJpZ2h0KSwKLSB5b3VyIEZpbGVzL1Bsb3RzL1BhY2thZ2VzL0hlbHAvVmlld2VyIChib3R0b20tcmlnaHQpLCBhbmQKLSB0aGUgUiBDb25zb2xlIChib3R0b20tbGVmdCkuCgpXYW50IHRvIGN1c3RvbWl6ZSB0aGlzPwoKVG9vbHMgLT4gR2xvYmFsIE9wdGlvbnMgLT4gUGFuZSBMYXlvdXQKCkFkdmFudGFnZXMgb2YgYW4gSURFCgotIGFsbCBpbiBvbmUgd2luZG93IQotIGtleWJvYXJkIHNob3J0Y3V0cywgYXV0b2NvbXBsZXRpb24sIGhpZ2hsaWdodGluZyAtPiB0eXBlIGVhc2llciwgZG8gbGVzcyBlcnJvcnMKCgojIyBGb2xkZXIgc3RydWN0dXJlCgpJdCBpcyBnb29kIHByYWN0aWNlIHRvIGtlZXAgYSBzZXQgb2YgcmVsYXRlZCBkYXRhLCBhbmFseXNlcywgYW5kIHRleHQgc2VsZi1jb250YWluZWQgaW4gYSBzaW5nbGUgZm9sZGVyLCBjYWxsZWQgdGhlIHdvcmtpbmcgZGlyZWN0b3J5LgoKV2h5PyAKCiogRWFzaWVyIHRvIGhhdmUgInNlbGYtY29udGFpbmVkIiByZXNlYXJjaCB1bml0cyEKKiBBIHByb2plY3QgImRvZXMgbm90IGludGVyZmVyZSIgd2l0aCBvdGhlciBwcm9qZWN0cwoqIEdpdmVzIGEgc3RydWN0dXJlLCBlYXNpZXIgdG8gZmluZCB0aGluZ3MsIHVzZSwgcmV1c2UKKiBTb21lb25lIGVsc2UgKGluY2x1ZGluZyBmdXR1cmUgeW91KSBjYW4gdW5kZXJzdGFuZCB3aGF0IGdvZXMgb24KCkhvdz8KClJTdHVkaW8gcHJvamVjdHMhICAKQ3VzdG9tIHNldHRpbmdzLCBwZXIgcHJvamVjdC4KCkxldCdzIGNyZWF0ZSBvbmUgbGl2ZSBub3cgLSBhbmQgaGF2ZSB0aGUgd29ya3NwYWNlIE5PVCBzYXZlZAoKV2hhdCBpcyB0aGUgYmVzdCBzdHJ1Y3R1cmU/CgpPbmUsIHVzZWQgY29uc2lzdGVudGx5IC0gbm90IGdvbm5hIHRvdWNoIG9uIG5hbWluZyB0aGluZ3MgYXMgaXQgY2FuIGdldCBob3QgcXVpY2tseSA6KQoKV2l0aCBSLi4uCgpgZGlyLmNyZWF0ZSgpYAoKYGZpbGUuZWRpdCgpYAoKCldoZXJlIGFtIEkgZG9pbmcgdGhpbmdzPwoKVGhlIHdvcmtpbmcgZGlyZWN0b3J5IGlzIHRoZSBwbGFjZSBmcm9tIHdoZXJlIFIgd2lsbCBiZSBsb29raW5nIGZvciBhbmQgc2F2aW5nIHRoZSBmaWxlcy4gCgpgZ2V0d2QoKWAvYHNldHdkKClgLCBidXQgbm90IGluIHlvdXIgc2NyaXB0cyAoZmFpbHMgb24gb3RoZXJzJyBjb21wdXRlcnMhKQoKIyMgSW50ZXJhY3Rpbmcgd2l0aCBSCgpJbnN0cnVjdGlvbnMsIGNvbW1hbmRzLgoKU2NyaXB0cywgY29uc29sZSAtIHVzZSB0aGUgZWRpdG9yIGFuZCBoYXZlIGEgY29tcGxldGUgcmVjb3JkIG9uIHdoYXQgeW91IGRpZCEKClNob3J0Y3V0cyBGVFchCgpFdmVuIGJldHRlcjogUmVwcm9kdWNpYmxlIGRvY3VtZW50cywgd2l0aCBSIE1hcmtkb3duCgpOaWNlIHJlc291cmNlcyBvbiB0b3A6CiogUlN0dWRpbyBjaGVhdHNoZWV0IGFib3V0IHRoZSBSU3R1ZGlvIElERSEKKiB0aGUgaW50ZXJuZXQvcnN0YXRzIGNvbW11bml0eSEKCgojIyBTZWVraW5nIGhlbHAKCiogPwoqID8/CiogYnVpbHQtaW4gUlN0dWRpbyBoZWxwIGludGVyZmFjZSAtIGFuZCBzaG9ydGN1dHMhCgoKIyMjIFdoZXJlIHRvIGFzayBmb3IgaGVscD8KCiogeW91ciBuZWlnaGJvdXIgLSBjb3ZpZC1jb25mb3JtLCBkbyBpbnRlcmFjdCB3aXRoaW4gZWFjaCBvdGhlciEKKiB5b3VyIGNvbGxlYWd1ZXMKKiByZG9jdW1lbnRhdGlvbi5vcmcgd2Vic2l0ZSAKKiB0aGUgd2ViOiBnb29nbGUsIFN0YWNrT3ZlcmZsb3cKClRoZSBtYWluIHBvaW50OiBkZXNjcmliZSB3ZWxsIHlvdXIgcHJvYmxlbSwgImhlbHAgb3RoZXJzIGhlbHAgeW91IgoKT3RoZXJzIG5lZWQgdG8gcmVwcm9kdWNlIHlvdXIgZXJyb3IgdG8gaGVscCB5b3UgYmV0dGVyOiBgc2F2ZVJEUygpYCwgYGRwdXQoKWAsIGBzZXNzaW9uSW5mbygpYAoKCiMjIFIgcGFja2FnZXMKClIgcGFja2FnZXMuLi4KCi0gYXJlIGZ1bmRhbWVudGFsIGNvbXBvbmVudHMgb2YgUiBlY29zeXN0ZW0KLSBleHRlbmQgYmFzZSBSIGZ1bmN0aW9uYWxpdHkgZm9yIGEgc3BlY2lmaWMgcHVycG9zZSAKLSBidW5kbGUgbmV3IGZ1bmN0aW9ucywgZGF0YSBzZXRzLCBhbmQgZG9jdW1lbnRhdGlvbgotIGFyZSBjb250cmlidXRlZCBieSBpbmRlcGVuZGVudCBkZXZlbG9wZXJzCi0gaGF2ZSBkZXBlbmRlbmN5IG1hbmFnZW1lbnQKClJlcG9zaXRvcmllczoKCi0gQ1JBTjogTWFuYWdlZCBvZmZpY2lhbCBwYWNrYWdlIHJlcG9zaXRvcnkgbmV0d29yayAKLSBCaW9jb25kdWN0b3I6IGN1cmF0ZWQgYmlvaW5mb3JtYXRpY3MgcGFja2FnZXMgKHZpZ25ldHRlcyBtYW5kYXRvcnksIGludGVncmF0ZWQgZWNvc3lzdGVtISkKLSBHaXRIdWI6IHVuLW1hbmFnZWQsIGJsZWVkaW5nIGVkZ2UgLSBidXQgYWxzbyBleGNlbGxlbnQgb25lcyAoImp1c3Qgbm90IG9uIENSQU4iKQoKCk15IGNvbnRyaWJ1dGlvbnMgc28gZmFyOgoKLSBmbG93Y2F0Y2hSIGBodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZmxvd2NhdGNoUi9gCi0gcGNhRXhwbG9yZXIgYGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9wY2FFeHBsb3Jlci9gCi0gaWRlYWwgYGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9pZGVhbC9gCi0gaVNFRSAoYGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9pU0VFYCkKLSBHZW5lVG9uaWMgKGBodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvR2VuZVRvbmljYCkKCgojIyMgIEhvdyB0byB1c2UgcGFja2FnZXMKCi0gSW5zdGFsbDogb25jZSAoZm9yIGV2ZXJ5IG1ham9yIFIgdmVyc2lvbikKLSBMb2FkOiBpbiBlYWNoIHNlc3Npb24KLSBVc2UgbGlrZSBhbnkgYmFzZSBSIGZ1bmN0aW9uYWxpdHkKCkZvciBCaW9jb25kdWN0b3IgcGFja2FnZXMuLi4KCmBgYHtyLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmxpYnJhcnkoIkJpb2NNYW5hZ2VyIikKQmlvY01hbmFnZXI6Omluc3RhbGwoKQpgYGAKCgpSZWxldmFudCBjb21tYW5kczogCgotIGBpbnN0YWxsLnBhY2thZ2VzKCJwYWNrYWdlbmFtZSIpYCAtIGNoZWNrIGl0IG9ubGluZSBhdCBDUkFOIQotIGBpbnN0YWxsZWQucGFja2FnZXMoKWAKLSBgLmxpYlBhdGhzKClgCi0gYHVwZGF0ZS5wYWNrYWdlcygpYAotIGBsaWJyYXJ5KCJwYWNrYWdlbmFtZSIpYAotIGBoZWxwKHBhY2thZ2U9InBhY2thZ2VuYW1lIilgLCBgZGF0YSgpYCwgYGJyb3dzZVZpZ25ldHRlcygpYCwgYHZpZ25ldHRlKClgLCBgY2l0YXRpb24oInBhY2thZ2VuYW1lIilgCgpTb21ldGhpbmcgeW91IG1pZ2h0IGhhdmUgYWxyZWFkeSBkb25lOgoKYGBge3IgZXZhbD1GQUxTRX0KQmlvY01hbmFnZXI6Omluc3RhbGwoIlN1bW1hcml6ZWRFeHBlcmltZW50IikKQmlvY01hbmFnZXI6Omluc3RhbGwoIkRFU2VxMiIpCmBgYAoKIyBTdGVwIDE6IEludHJvZHVjdGlvbiB0byBSCgpIZXJlIHdlIHdpbGwgdG91Y2ggb24gdGhlIGZpcnN0IGNvbW1hbmRzIGluIFIsIHNvIHRoYXQgeW91IGNhbgoKKiBEZWZpbmUgdGhlIGZvbGxvd2luZyB0ZXJtcyBhcyB0aGV5IHJlbGF0ZSB0byBSOiBvYmplY3QsIGFzc2lnbiwgY2FsbCwgZnVuY3Rpb24sIGFyZ3VtZW50cywgb3B0aW9ucy4KKiBBc3NpZ24gdmFsdWVzIHRvIG9iamVjdHMgaW4gUi4KKiBMZWFybiBob3cgdG8gbmFtZSBvYmplY3RzCiogVXNlIGNvbW1lbnRzIHRvIGluZm9ybSBzY3JpcHQuCiogU29sdmUgc2ltcGxlIGFyaXRobWV0aWMgb3BlcmF0aW9ucyBpbiBSLgoqIENhbGwgZnVuY3Rpb25zIGFuZCB1c2UgYXJndW1lbnRzIHRvIGNoYW5nZSB0aGVpciBkZWZhdWx0IG9wdGlvbnMuCiogSW5zcGVjdCB0aGUgY29udGVudCBvZiB2ZWN0b3JzIGFuZCBtYW5pcHVsYXRlIHRoZWlyIGNvbnRlbnQuCiogU3Vic2V0IGFuZCBleHRyYWN0IHZhbHVlcyBmcm9tIHZlY3RvcnMuCiogQW5hbHl6ZSB2ZWN0b3JzIHdpdGggbWlzc2luZyBkYXRhLgoKCiMjIFIgaXMgYSBwb3dlcmZ1bCBjYWxjdWxhdG9yCgouLi4gYnV0IG5vdCBqdXN0IHRoYXQuCgpUeXBlIHRoZSBmb2xsb3dpbmcKCmBgYHtyfQoyICsgMgpsb2coMikKMzQ3ICogNzM4NDEKNy8yCjclLyUyCjclJTIKYGBgCgoKIyMgYHIgZW1vOjpqaSgibm90ZXMiKWAgSGVscCEgYHIgZW1vOjpqaSgibm90ZXMiKWAKCmBgYHtyfQojIHRoaXMgY2FsbHMgdGhlIGhlbHAgZm9yIGEgZnVuY3Rpb24gdG8gcGxvdCBhIGhpc3RvZ3JhbQo/aGlzdAojIHRoaXMgaXMganVzdCB0aGUgc2FtZQpoZWxwKGhpc3QpICMjIHdoYXQgYWJvdXQgPz8KYGBgCgpgYGB7cn0KP2Fwcm9wb3MKYXByb3Bvcygicm93IikKYGBgCgotIGludGVncmF0ZWQgaGVscCBzeXN0ZW0sIHdpdGggZXhlY3V0YWJsZSBleGFtcGxlcwotIChmb3Igc29tZSBwYWNrYWdlcykgdmlnbmV0dGVzICh0eXBpY2FsIHByb2JsZW0sIGNvbW1hbmRzLCBhbmQgd29ya2Zsb3cpCi0gQ1JBTiBUYXNrIFZpZXdzOiBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvdmlld3MvCi0gQm9va3MhCi0gQ291cnNlcyEKLSBPbmxpbmU6IG1haWxpbmcgbGlzdHMsIGZvcnVtcyAoU3RhY2tPdmVyZmxvdywgLi4uKSwgYmxvZ3MsIFR3aXR0ZXIgKGAjcnN0YXRzYCkKCgojIyBZb3VyIHN0YXJ0aW5nIHZvY2FidWxhcnkgLSBhLmsuYS4gRXhlcmNpc2UgU2Vzc2lvbiAwCgotIGBnZXR3ZCgpYCBhbmQgYHNldHdkKClgIC0gVGFiIGlzIHlvdXIgZnJpZW5kCi0gYCA8LSBgLCBgID0gYDogdGhlIGFzc2lnbm1lbnQgb3BlcmF0b3IKLSBgbHMoKWAsIGBybSgpYAotIGBzdHIoKWAKLSBgZXhhbXBsZSgpYCwgYGhlbHAoKWAvYD9bZnVuY3Rpb25dYAotIGBwcmludCgpYAotIGBxKClgL2BxdWl0KClgCi0gbG9naWNhbCBvcGVyYXRvcnM6IGBUUlVFYCxgRkFMU0VgLGAhYCxgPT1gLGAhPWAsYDxgLGA+YCxgPD1gLGA+PWAsYHxgLGAmYCxgeG9yKClgCi0gYGMoKWAKLSBkYXRhIGhhdmUgaGVscCBpdGVtcyB0b286IGUuZy4gYGNhcnNgCgpGaW5kIG91dCB3aGF0IHRoZXNlIGRvIQoKIyMjIEV4ZXJjaXNlIFNlc3Npb24gMCAtIFNvbHV0aW9ucwoKYGBge3J9Cj9nZXR3ZAo/c2V0d2QKP2A8LWAKaGVscChscykKaGVscChybSkKP3N0cgo/ZXhhbXBsZQpoZWxwKGhlbHApCj9wcmludApoZWxwKHF1aXQpCj9jCj9jYXJzCmBgYAoKCgojIyBNYWtlIHlvdXIgbGlmZSBlYXNpZXIgLSBOb3RlcyBmb3IgeW91ciBmdXR1cmUgc2VsZgoKLSBhZGQgY29tbWVudHMgYW5kIGRvY3VtZW50IHlvdXIgb3duIGNvZGUKCmBgYHtyfQojIFRoaXMgaXMgYSBjb21tZW50CmBgYAoKLSB3cml0ZSBjbGVhbiBjb2RlIC0gdXNlIHNwYWNlcywgaW5kZW50YXRpb24KLSB1c2UgYW4gZWRpdG9yIHdpdGggc3ludGF4IGhpZ2hsaWdodGluZy9zb21lIGZvcm0gb2YgYXV0b2NvbXBsZXRpb24gCgpDYXJlZnVsIGhlcmU6CgotIFIgaXMgY2FzZSBzZW5zaXRpdmUgYW5kIGhhcyB6ZXJvLXRvbGVyYW5jZSB3aXRoIG1pcy1zcGVsbGVkIG5hbWVzCi0gcGFyZW50aGVzaXM6IG9wZW4gKmFuZCogY2xvc2UgdGhlbQotIHNwZWNpYWwgYXR0ZW50aW9uIHdpdGggbWlzc2luZyB2YWx1ZXMsIGZhY3RvcnMgVlMgc3RyaW5nczogUiBpcyBjbGV2ZXIsIGJ1dCB5b3UgbWlnaHQgdGhpbmsgZGlmZmVyZW50bHkKLSBkbyBub3QgYmUgc3Rpbmd5IHdpdGggcGFyZW50aGVzZXMgLSBpZiB0aGlzIGhlbHBzIHlvdQotIHNhbWUgZ29lcyB3aXRoIGNvbW1lbnRzIC0geW91ciBjb2xsZWFndWVzIGFuZCB5b3VyIGZ1dHVyZSBzZWxmIHdpbGwgdGhhbmsgeW91CgoKIyMgRXhlcmNpc2Ugc2Vzc2lvbiAxCgpHcmFiIHNvbWUgbWluaS1wb3N0aXQhCgotIGZpbmQgb3V0IG1vcmUgYWJvdXQgdGhlIGBpcmlzYCBkYXRhc2V0LiBXaGF0IGlzIGl0IGFib3V0IGF0IGFsbD8gSG93IG1hbnkgdmFyaWFibGVzIGFyZSBpbmNsdWRlZD8gSG93IG1hbnkgb2JzZXJ2YXRpb25zPwotIGByZXBgbGljYXRlISBmaW5kIG91dCBhIGZ1bmN0aW9uIHRoYXQgYHJlcGBsaWNhdGVzIGVsZW1lbnRzIG9mIGEgdmVjdG9yIHRvIHByb2R1Y2UgdGhpcwoKYGBge3IgZXZhbD1GQUxTRX0KMSAxIDEgMQpgYGAKCkJPTlVTOiAuLi4gYW5kIHRoaXMgCiAKYGBge3IgZXZhbD1GQUxTRX0KMSAyIDMgNCA1IDEgMiAzIDQgNSAxIDIgMyA0IDUKYGBgCgojIyMgRXhlcmNpc2UgU2Vzc2lvbiAxIC0gU29sdXRpb25zCgo8ZGV0YWlscz4KCmBgYHtyfQpyZXAoMSw0KQoKcmVwKGMoMSwyLDMsNCw1KSwzKQpgYGAKCjwvZGV0YWlscz4KCiMjIERhdGEgdHlwZXMgCgpSIGNhbiByZWNvZ25pemUgZGlmZmVyZW50IGdlbmVyYWwgdHlwZXMgb2YgZGF0YQoKLSBudW1iZXJzIChgbnVtZXJpY2ApCi0gY2hhcmFjdGVyIHN0cmluZ3MgKHRleHQpCi0gbG9naWNhbCAoZS5nLiBgY2xhc3MoVFJVRSlgKQotIGZhY3RvcnMgKCJpbnRlZ2VycyB3aXRoIGEgc2V0IG9mIGxhYmVscyIpIC0gaXQgaXMgY2F0ZWdvcmljYWwgZGF0YSEKCi0gc3BlY2lhbCBvbmVzOiBkYXRlcywgdGltZSwgLi4uCiAKV2hlbiBhbmQgd2hlcmUgdG8gdXNlIHdoaWNoPyAKCgojIyBJIHdhcyBjdXJpb3VzIGFib3V0IHlvdQoKSW4gYSBwcmV2aW91cyBlZGl0aW9uIG9mIGEgc2ltaWxhciBjb3Vyc2UsIEkgd2FudGVkIHRvIGtub3c6CgotIEhvdyBvbGQgYXJlIHlvdT8KLSBXaGF0IGlzIHlvdXIgY3VycmVudCBhY2FkZW1pYyBsZXZlbD8gKFBJLCBQb3N0ZG9jLCBQaEQsIG1hc3RlcikKLSBXaGF0IGlzIHlvdXIgY3VycmVudCBrbm93bGVkZ2UgbGV2ZWwgb2YgUj8gKHByby1nb29kLWludGVybWVkaWF0ZS1wb29yLW5vbmUpCi0gV2hhdCBpcyB5b3VyIGtub3dsZWRnZSBvZiBwcm9ncmFtbWluZyBsYW5ndWFnZXMgaW4gZ2VuZXJhbD8KLSBXaGF0IGlzIHlvdXIgZXhwZXJpZW5jZSBsZXZlbCB3aXRoIGdlbm9taWNzIGFuZCBSTkEtc2VxIGRhdGE/Ci0gSG93IGZhbWlsaWFyIGFyZSB5b3Ugd2l0aCBtb2dvbiBhbmQgcGFyYWxsZWwgY29tcHV0aW5nPyAoSSBhbSBhIHJlZ3VsYXIgdXNlci9PbmNlIGluIGEgd2hpbGUgaSB1c2VkIGl0L0kga25vdyBpdCBleGlzdHMvSSBoZWFyZCB3ZSBoYWQgc29tZSBzZXJ2ZXJzIGFyb3VuZC9JcyB0aGlzIHN1cHBvc2VkIHRvIGJlIGluIHRoZSBjbG91ZD8hKQotIFdoYXQgYXJlIHlvdXIgZXhwZWN0YXRpb25zIGZyb20gdGhlIGNvdXJzZT8gCgojIEJvbnVzIFN0ZXA6IHJlcHJvZHVjaWJsZSByZXBvcnRzCgpPdXIgYWltczogCgotIFVuZGVyc3RhbmQgd2hhdCBSIE1hcmtkb3duIGlzIGFuZCB3aHkgeW91IHNob3VsZCB1c2UgaXQKLSBMZWFybiBob3cgdG8gY29uc3RydWN0IGFuIFIgTWFya2Rvd24gZmlsZQotIEV4cG9ydCBhbiBSIE1hcmtkb3duIGZpbGUgaW50byBtYW55IGZpbGUgZm9ybWF0cwotIC0tPiBZb3UgYXJlIGFsbCBzZXQgdG8gdXNlIFJtZCB0byBkb2N1bWVudCBhbnkgb2YgeW91ciBhbmFseXNlcyEKCiMjIFJlcHJvZHVjaWJsZSByZXBvcnRzIHdpdGggUiBNYXJrZG93bgoKUiBNYXJrZG93biBhbGxvd3MgeW91IHRvIGNyZWF0ZSBkb2N1bWVudHMgdGhhdCBzZXJ2ZSBhcyBhIG5lYXQgcmVjb3JkIG9mIHlvdXIgYW5hbHlzaXMuIAoKV2h5PwoKLSB3ZSB3YW50IG90aGVyIHJlc2VhcmNoZXJzIHRvIGVhc2lseSB1bmRlcnN0YW5kIHdoYXQgd2UgZGlkIGluIG91ciBhbmFseXNpcywgb3RoZXJ3aXNlIG5vYm9keSBjYW4gYmUgY2VydGFpbiB0aGF0IHlvdSBhbmFseXNlZCB5b3VyIGRhdGEgcHJvcGVybHkgKHlheSwgcmVwcm9kdWNpYmxlIHJlc2VhcmNoISkKLSBjcmVhdGUgYW4gUiBtYXJrZG93biBkb2N1bWVudCBhcyBhbiBhcHBlbmRpeCB0byBhIHBhcGVyIG9yIHByb2plY3QgYXNzaWdubWVudCwgdXBsb2FkIGl0IHRvIGFuIG9ubGluZSByZXBvc2l0b3J5IHN1Y2ggYXMgR2l0aHViLCBvciBzaW1wbHkgdG8ga2VlcCBhcyBhIHBlcnNvbmFsIHJlY29yZCAoKmZ1dHVyZSB5b3UqIHdpbGwgdGhhbmsgKnByZXNlbnQgeW91KiBmb3IgdGhpcykKClRoZSBrZXkgcG9pbnQgaXMuLi4KClIgTWFya2Rvd24gZG9jdW1lbnRzIHByZXNlbnQgeW91ciBjb2RlIGFsb25nc2lkZSBpdHMgb3V0cHV0IChncmFwaHMsIHRhYmxlcywgZXRjLikgd2l0aCBjb252ZW50aW9uYWwgdGV4dCB0byBleHBsYWluIGl0LCBhIGJpdCBsaWtlIGEgbm90ZWJvb2suIFRvIGRvIHRoaXMsIFIgTWFya2Rvd24gdXNlcyAqKm1hcmtkb3duKiogc3ludGF4LiAKCgoKIyMgTWFya2Rvd24KCk1hcmtkb3duIGlzIGEgdmVyeSBzaW1wbGUgKm1hcmt1cCogbGFuZ3VhZ2Ugd2hpY2ggcHJvdmlkZXMgbWV0aG9kcyBmb3IgY3JlYXRpbmcgZG9jdW1lbnRzIHdpdGggaGVhZGVycywgaW1hZ2VzLCBsaW5rcyBldGMuIGZyb20gcGxhaW4gdGV4dCBmaWxlcywgd2hpbGUga2VlcGluZyB0aGUgb3JpZ2luYWwgcGxhaW4gdGV4dCBmaWxlIGVhc3kgdG8gcmVhZC4gCgpZb3UgY2FuIGNvbnZlcnQgTWFya2Rvd24gZG9jdW1lbnRzIHRvIG1hbnkgb3RoZXIgZmlsZSB0eXBlcyBsaWtlIGAuaHRtbGAgb3IgYC5wZGZgIHRvIGRpc3BsYXkgdGhlIGhlYWRlcnMsIGltYWdlcyBldGMuLgoKSXQgbWlnaHQgc291bmQgY29tcGxpY2F0ZWQuIEJ1dCAqcmVhbGx5KiBpc24ndCwgCgohW10oaHR0cHM6Ly9tZWRpYS5naXBoeS5jb20vbWVkaWEvMjZCUk5vUUo1YlJjWlM4SG0vZ2lwaHkuZ2lmKQoKRmlyc3QgdGhpbmdzIGZpcnN0OiBpbnN0YWxsIHRoZSByZXF1aXJlZCBzb2Z0d2FyZQoKLSBSIGFuZCBSU3R1ZGlvIChndWVzcyB5b3UgaGF2ZSBpdCBhbHJlYWR5KQoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygicm1hcmtkb3duIikKYGBgCgpgYGB7cn0KbGlicmFyeShybWFya2Rvd24pCmBgYAoKLSBga25pdHJgIGNvbWVzIGFsb25nLCBgcGFuZG9jYCB0b28uIFlvdSBzaG91bGQgcXVpY2tseSBiZSBhbGwgc2V0IQoKIyMgQmFzaWNzIG9mIG1hcmtkb3duCgpBQkMgaGVyZSwgbGV0J3MgZ28gdGhyb3VnaCBpdDoKCmh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vYXV0aG9yaW5nX2Jhc2ljcy5odG1sCgpwbHVzLi4uIGEgYmVhdXRpZnVsIGNoZWF0IHNoZWV0IGlzIHRoZXJlIGZvciB5b3UhCgpodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2xlc3Nvbi0xNS5odG1sCgpodHRwOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE2LzAzL3JtYXJrZG93bi1jaGVhdHNoZWV0LTIuMC5wZGYKClVzaW5nIExhVGVYPyBObyBwcm9ibGVtLCB5b3UgY2FuIHVzZSAkXExhVGVYJCBoZXJlIGFzIHdlbGwhCgokXGxlZnQoIGYoeCkgPSBcc3VtX3tpPTB9XntufSBcZnJhY3thX2l9ezEreH0gXHJpZ2h0KSQKCldoYXQgY2FuIHlvdSBkbyB3aXRoIFJtYXJrZG93bj8KCmh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vZ2FsbGVyeS5odG1sCgpodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2Zvcm1hdHMuaHRtbAoKCiMjIExldCdzIGNyZWF0ZSBvbmUgdG9nZXRoZXIhCgojIyMgV2h5IGlzIFJtZCBiZXR0ZXIgdGhhbiBSCgohW10oaHR0cHM6Ly9pLmltZ2ZsaXAuY29tLzIydTU2NS5qcGcpCgpUaGUgcHJpY2UgdG8gcGF5IHRvIGhhdmUgYW4gUm1kIGRvY3VtZW50IGlzIHNvb29vbyBzbWFsbCAtIGFuZCBmb3IgdGhhdCwgeW91IGdldAoKLSBjb2RlLCB0ZXh0LCBvdXRwdXQgYWxsIHRvZ2V0aGVyCi0gb25lIGZpbGUgb25seSAtIG5vIG5lZWQgdG8gZ2V0IGxvc3QKLSBpdCBldmVuIGxvb2tzIG5pY2UgOikKCgojIyMgQ3JlYXRlIGFuIFJtYXJrZG93biBmaWxlIAoKVG8gY3JlYXRlIGEgbmV3IFIgTWFya2Rvd24gZmlsZSAoYC5SbWRgKSwgc2VsZWN0IGBGaWxlIC0+IE5ldyBGaWxlIC0+IFIgTWFya2Rvd24uLi5gIGluIFJTdHVkaW8sIHRoZW4gY2hvb3NlIHRoZSBmaWxlIHR5cGUgeW91IHdhbnQgdG8gY3JlYXRlLiAKClRoZSBuZXdseSBjcmVhdGVkIGAuUm1kYCBmaWxlIGNvbWVzIHdpdGggYmFzaWMgaW5zdHJ1Y3Rpb25zIGJ1dCB3ZSB3YW50IHRvIGNyZWF0ZSBvdXIgb3duIFIgTWFya2Rvd24gc2NyaXB0LCBzbyBsZXQncyBnZXQgdG8ga25vdyB0aGUgZGlmZmVyZW50IHBhcnRzIG9mIGFuIFJtZCBmaWxlCgotIEFuIChvcHRpb25hbCkgWUFNTCBoZWFkZXIgc3Vycm91bmRlZCBieSBgLS0tYHMKLSBSIGNvZGUgY2h1bmtzIHN1cnJvdW5kZWQgYnkgYmFja3RpY2tzIChgYGApCi0gdGV4dCBtaXhlZCB3aXRoIHNpbXBsZSB0ZXh0IGZvcm1hdHRpbmcKCiMjIyBJbnNlcnRpbmcgZmlndXJlcyAKClVoLCB5b3UgY2FuIGluc2VydCBmaWd1cmVzIGFsc28gbGlrZSB0aGlzCgpgYGAKIVtdKGltYWdlcy9ncmNhdC5wbmcpCmBgYAoKIVtdKGltYWdlcy9ncmNhdC5wbmcpCgojIyBJbnNlcnQgdGV4dCBhbmQgY29kZSAtIGFueSB0ZXh0LCBhbnkgY29kZQoKYGBgYApgYGB7cn0KbiA8LSAxMApybm9ybShuKQpgYGAKYGBgYAoKU2hvcnRjdXQ6IGBDdHJsICsgQWx0ICsgSWAgICAgCgpJbnB1dCBjb2RlOiB5b3UgY2FuIHVzZSBtdWx0aXBsZSBsYW5ndWFnZXMgaW5jbHVkaW5nIFIsIFB5dGhvbiwgYW5kIFNRTCwgbWFueSBtb3JlIChzcGVjaWZ5IHRoZSBsYW5ndWFnZSBpbiB0aGUgY2h1bmsgb3B0aW9ucykKCgpJbmxpbmUgY29kZSBjYW4gYmUgYWRkZWQgd2l0aCBgYCBgcgoxKzFgIGBgCgoKIyMjIENodW5rIG9wdGlvbnMKCkRlYXRpbGVkIHZlcnkgbmljZWx5IGhlcmU6IGh0dHBzOi8veWlodWkubmFtZS9rbml0ci9vcHRpb25zLwoKQSBzaW1wbGUgc2V0IG9mIG9wdGlvbnMgd2hpY2ggeW91IGNhbiB1c2UgZm9yIG1hbnkgZG9jdW1lbnRzOgoKYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0Kc2V0LnNlZWQoNDIpCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBjb21tZW50ID0gTkEsCiAgZmlnLmFsaWduID0gImNlbnRlciIsCiAgZmlnLndpZHRoID0gNywKICBmaWcuaGVpZ2h0ID0gNywKICB3YXJuaW5nID0gRkFMU0UsCiAgZXZhbCA9IFRSVUUKKQpgYGAKCgojIyMgS25pdCEKClVzZSB0aGUgYEtuaXRgIGJ1dHRvbiBpbiB0aGUgUlN0dWRpbyBJREUgdG8gcmVuZGVyIHRoZSBmaWxlIGFuZCBwcmV2aWV3IHRoZSBvdXRwdXQgd2l0aCBhIHNpbmdsZSBjbGljayBvciBrZXlib2FyZCBzaG9ydGN1dCAoYEN0cmwgKyBTaGlmdCArIEtgKS4KClRvIGdlbmVyYXRlIGEgcmVwb3J0IGZyb20gdGhlIGZpbGUsIHJ1biB0aGUgYHJlbmRlcmAgY29tbWFuZCAod29ya3MgYWxzbyBvdXRzaWRlIG9mIFJTdHVkaW8pOgoKYGBge3IgZXZhbD1GQUxTRX0KbGlicmFyeSgicm1hcmtkb3duIikKcm1hcmtkb3duOjpyZW5kZXIoInlvdXJmaWxlLlJtZCIpCmBgYAoKSXQgd2FzIGEgZGVlcCBkaXZlLCBidXQgbm93Li4uCgotIFlvdSBhcmUgZmFtaWxpYXIgd2l0aCB0aGUgTWFya2Rvd24gc3ludGF4IGFuZCBjb2RlIGNodW5rIHJ1bGVzLgotIFlvdSBjYW4gaW5jbHVkZSBmaWd1cmVzIGFuZCB0YWJsZXMgaW4geW91ciBNYXJrZG93biByZXBvcnRzLgotIFlvdSBjYW4gY3JlYXRlIFIgTWFya2Rvd24gZmlsZXMgYW5kIGV4cG9ydCB0aGVtIHRvIHBkZiBvciBodG1sIGZpbGVzLgoKCgoKIyMgKE11Y2gpIG1vcmUgb24gUm1hcmtkb3duCgotIGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vCi0gaHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDA3X2ZpcnN0LXVzZS1ybWFya2Rvd24uaHRtbAotIGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vbGVzc29uLTEuaHRtbAotIC4uLiB0byBodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2xlc3Nvbi0xNS5odG1sCi0gaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9hcnRpY2xlcy5odG1sCgpZb3UgY2FuIGRvIG11Y2ggbXVjaCBtb3JlIChwcmVzZW50YXRpb25zLCB3ZWJzaXRlcywgbWFudXNjcmlwdHMsLi4uKQoKIyMgRXhlcmNpc2Ugc2Vzc2lvbiBCb251cwoKLSBjcmVhdGUgYSBuZXcgUm1hcmtkb3duIGRvY3VtZW50Ci0gY2FuIHlvdSBmaW5kIG91dCBob3cgdG8gZ2VuZXJhdGUgYSB3b3JkIGRvY3VtZW50IGFzIG91dHB1dD8KLSBpbnNlcnQgc29tZSBjb2RlIHlvdSBwcmV2aW91c2x5IHVzZWQgZm9yIGV4cGxvcmluZyB0aGUgc21hbGwgc3VydmV5IGRhdGEgLSByZW1lbWJlciwgYSBmcmVzaCBzZXNzaW9uIGlzIHJ1biB3aGVuIGtuaXR0aW5nLCBzbyB5b3UgbmVlZCB0aGUgY29tbWFuZHMgZnJvbSB0aGUgdmVyeSBzdGFydCEKCiMjIyBFeGVyY2lzZSBTZXNzaW9uIEJvbnVzIC0gU29sdXRpb25zCgotIGBGaWxlIC0+IE5ldyBGaWxlIC0+IFIgTWFya2Rvd24uLi5gIGluIFJTdHVkaW8KLSBhZGQgdGhpcyBpbiB0aGUgeWFtbCBoZWFkZXIKCmBgYApvdXRwdXQ6CiAgd29yZF9kb2N1bWVudApgYGAKCgoKCiMgU3RlcCAyOiBEYXRhIGluLCBkYXRhIG91dAoKIyMgSW1wb3J0aW5nIGRhdGEgaW4gUgoKODAtMjA/IDkwLTEwPyBJbXBvcnQsIGNsZWFuLCBwcmVwYXJlLCB0cmFuc2Zvcm0geW91ciBkYXRhCgpTb3VyY2VzOgoKLSBGaWxlcywgQ2xpcGJvYXJkLCBVUkwKLSAqKlBsYWluIHRleHQgZmlsZTogQ29tbWEtc2VwYXJhdGVkLCB0YWItZGVsaW1pdGVkLCAuLi4qKgotIFIgZm9ybWF0IGZpbGUKLSBTQVMgLyBTdGF0YSAvIFNQU1MgZmlsZTogcGFja2FnZSBgaGF2ZW5gCi0gU3ByZWFkc2hlZXQgKEV4Y2VsKTogcGFja2FnZSBgcmVhZHhsYCAtIGhpZ2hseSByZWNvbW1lbmRlZCEKLSBEYXRhYmFzZTogUlNRTGl0ZSwgUlBvc3RncmVTUUwsIFJNeVNRTCwgLi4uCgoKIyMgVGhlIHZvY2FidWxhcnkgb2YgaW1wb3J0aW5nCgouLi4gYW5kIGV4cG9ydGluZwoKLSBgcmVhZC50YWJsZSgpYCxgd3JpdGUudGFibGUoKWAgKyBgcmVhZC5jc3Z8ZGVsaW1gCi0gdGhlIG9wdGlvbiBgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRWAKLSBgbG9hZCgpYCxgc2F2ZSgpYC9gcmVhZFJEUygpYCxgc2F2ZVJEUygpYAotIHZpYSBgaGF2ZW5gIDogYHJlYWRfc2FzKClgLGByZWFkX3Nwc3MoKWAgL2B3cml0ZV9zYXMoKWAsYHdyaXRlX3NhdigpYAotIHZpYSBgcmVhZHhsYDogYHJlYWRfZXhjZWwoKWAKCkNoZWNrIG91dCB0aGVpciBkb2N1bWVudGF0aW9uIHBhZ2VzIQoKT3RoZXIgb3B0aW9uczogYHJpb2AsIFJTdHVkaW8gR1VJCgoKCiMjIFRha2UgYSBsb29rIGF0IHRoZSBkYXRhIAoKR28gdG8gaHR0cHM6Ly9naXRodWIuY29tL2ZlZGVyaWNvbWFyaW5pL3JiaW9jMjAxNgoKLT4gYGluc3QvZXh0ZGF0YWAKCi0+IGBzdXJ2ZXlfcmVzcG9uc2VzLmNzdmAsIGluIGl0cyByYXcgZm9ybWF0CgpZb3UgY2FuIGxvYWQgaXQgZGlyZWN0bHkgbGlrZSB0aGlzCgpgYGB7cn0Kc3VydmV5cmJpb2MgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9mZWRlcmljb21hcmluaS9yYmlvYzIwMTYvbWFzdGVyL2luc3QvZXh0ZGF0YS9zdXJ2ZXlfcmVzcG9uc2VzLmNzdiIpCmBgYAoKT3IgaW5zdGFsbCB0aGUgcGFja2FnZSBhbmQgbG9hZCBpdCBmcm9tIHRoZXJlCgpgYGB7ciBldmFsPUZBTFNFfQpsaWJyYXJ5KCJkZXZ0b29scyIpCmluc3RhbGxfZ2l0aHViKCJmZWRlcmljb21hcmluaS9yYmlvYzIwMTYiKQpsaWJyYXJ5KCJyYmlvYzIwMTYiKQpkYXRhKHN1cnZleXJiaW9jKQpgYGAKCgojIyBJbnB1dCBkYXRhOiBTdGVwIGJ5IHN0ZXAsIGJ5IGhhbmQ/CgpTb21ldGltZXMgeW91ciBkYXRhIGlzIGVpdGhlciBzbWFsbCBhbmQvb3Igbm90IGluIGFuIEV4Y2VsLWxpa2UgdGFidWxhciBmb3JtYXQuCgpXaGF0IHRvIGRvPyBZb3UgYGNgb21iaW5lIHRoZSBlbGVtZW50cyB0b2dldGhlciEKCmBgYHtyIGV2YWw9VFJVRX0KUTEgPC0gYygyOCwyNywzMywzMiwyOSkKIyBzaG91bGQgcmV0dXJuIHRoaXMKUTEKClEyIDwtIGMoIlBoRCBzdHVkZW50IiwiUGhEIHN0dWRlbnQiLCAiUG9zdGRvYyIsIlBoRCBzdHVkZW50IiwiUGhEIHN0dWRlbnQiKQpRMgojIC4uLiBhbmQgc28gb24KYGBgCgoKIyMgQ29tYmluZSB0aGUgdmFyaWFibGVzIHRvIGEgbWF0cml4CgpXZSBoYXZlIHNlZW4gYGMoKWAuIFdlIGFsc28gaGF2ZQoKLSBgY2JpbmRgCi0gYHJiaW5kYAoKYGBge3IsIGV2YWw9VFJVRX0KZmlyc3RUd28gPC0gY2JpbmQoUTEsUTIpCmZpcnN0VHdvCmBgYAoKYGBge3J9CnJiaW5kKFExLFEyKQpgYGAKCklzIHRoaXMgd2hhdCB5b3Ugd2FudGVkPwoKIyMgQXBwbHlpbmcgdGhlIGZpcnN0IGZ1bmN0aW9ucwoKQnV0IGZpcnN0LCB3aGF0IGNhbiB5b3UgZG8gb24gdGhlc2Ugb2JqZWN0cz8KCmBgYHtyIGVycm9yPVRSVUV9CnN1bShRMSkKc3VtKFEyKQpzdW1tYXJ5KFExKQpzdW1tYXJ5KFEyKQpzdHIoUTEpCnN0cihRMikKbWVhbihRMSkKZGltKGZpcnN0VHdvKQpmaXJzdFR3b1ssMV0KbWVhbihmaXJzdFR3b1ssMV0pICMgV2h5LCBkYW1uLCB3aHk/IE1lZXQgY29lcmNpb24KY2xhc3MoZmlyc3RUd28pCmBgYAoKIyMgYG1hdHJpeGAsIGBkYXRhLmZyYW1lYCBhbmQgYGxpc3RgCgotIGEgYG1hdHJpeGAgY2FuIGNvbnRhaW4gb25lIHR5cGUgb2YgZGF0YSAtIGlmIG51bWVyaWMsIHlvdSB1bmxlYXNoIGFsbCB0aGUgbWF0cml4IGFsZ2VicmEgcG93ZXIhCi0gYSBgZGF0YS5mcmFtZWAgY2FuIHN0b3JlIG1vcmUgdHlwZXMgb2YgZGF0YSAob25lIHBlciBjb2x1bW4pCi0gYSBgbGlzdGAgaXMgbGlrZSBhIGJpZyBib3ggd2hlcmUgeW91IGNhbiBwdXQgYW55dGhpbmcgLSBidXQgdGhpcyBpcyBub3QgYWx3YXlzIHdoYXQgeW91IHdhbnQKCldoYXQgaXMgYmVzdD8KCkxldCdzIHRyeSB3aXRoIGEgYGxpc3RgCgpgYGB7ciBldmFsPVRSVUV9ClEzIDwtIGMoImludGVybWVkaWF0ZSIsInBvb3IiLCJnb29kIiwibm9uZSIsImludGVybWVkaWF0ZSIpCm15bGlzdCA8LSBsaXN0KFExLFEyLFEzKQpteWxpc3QKYGBgCgpgYGB7cn0KIyMgYWNjZXNzIHlvdXIgZWxlbWVudHMgd2l0aApteWxpc3RbWzFdXQpteWxpc3RbWzFdXVsyXQpgYGAKCgpIb3cgZG8gd2UgY3JlYXRlIGEgYGRhdGEuZnJhbWVgPwoKYGBge3IgZXZhbD1UUlVFfQpteWRmIDwtIGRhdGEuZnJhbWUoYWdlID0gUTEsCiAgICAgICAgICAgICAgICAgICBsZXZlbCA9IFEyLAogICAgICAgICAgICAgICAgICAgcmV4cCA9IFEzKQpteWRmCmNsYXNzKG15ZGYkYWdlKQpgYGAKCgojIyMgRXhwbG9yaW5nIGEgYGRhdGEuZnJhbWVgCgoKYGBge3J9Cm15ZGYkYWdlICAgIyBpdCdzIGFsbCBhYm91dCB0aGUgbW9uZXkgOikKbXlkZlssMV0KbmFtZXMobXlkZikKcm93bmFtZXMobXlkZikKZGltKG15ZGYpCm5yb3cobXlkZikKbmNvbChteWRmKQpgYGAKCmBgYHtyIHRpZHk9VFJVRX0Kc3VydmV5cmJpb2MgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9mZWRlcmljb21hcmluaS9yYmlvYzIwMTYvbWFzdGVyL2luc3QvZXh0ZGF0YS9zdXJ2ZXlfcmVzcG9uc2VzLmNzdiIpCmhlYWQoc3VydmV5cmJpb2MpCnRhaWwoc3VydmV5cmJpb2MpCm5hbWVzKHN1cnZleXJiaW9jKQojIFZpZXcoc3VydmV5cmJpb2MpCnN0cihzdXJ2ZXlyYmlvYykKc3VtbWFyeShzdXJ2ZXlyYmlvYykKc3VydmV5cmJpb2NbICwgXSAKYGBgCgoKCiMjIEV4ZXJjaXNlIHNlc3Npb24gMgoKVXNpbmcgdGhlIGBzdXJ2ZXlyYmlvY2Agb2JqZWN0OgoKLSBDYWxjdWxhdGUgdGhlIG1lYW4gYWdlIG9mIHRoZSBwYXJ0aWNpcGFudHMKLSBIb3cgbWFueSBwYXJ0aWNpcGFudHMgZGlkIGFjdHVhbGx5IHRha2UgcGFydCB0byB0aGUgc3VydmV5PwotIEhvdyBvbGQgd2FzIHRoZSBvbGRlc3QgcGFydGljaXBhbnQ/IChgbWF4YCBjYW4gYmUgeW91ciBoZWxwKQotIGB0YHJhbnNwb3NlIHRoZSBzdXJ2ZXkgZGF0YSBhbmQgYXNzaWduIGl0IHRvIGFub3RoZXIgdmFyaWFibGUKLSBDaGFuZ2UgdGhlIGNvbHVtbiBuYW1lcyBvZiB0aGlzIG9iamVjdCBhbmQgc2F2ZSB0aGlzIGRhdGEgc2V0IGFzIGEgdGFiLXNlcGFyYXRlZCBBU0NJSSBmaWxlCi0gQk9OVVM6IHdoYXQgd2FzIHRoZSB5b3VuZ2VzdCBwYXJ0aWNpcGFudCBleHBlY3Rpbmc/CgojIyMgRXhlcmNpc2UgU2Vzc2lvbiAyIC0gU29sdXRpb25zCgo8ZGV0YWlscz4KCmBgYHtyfQptZWFuKHN1cnZleXJiaW9jJFExKQoKbWF4KHN1cnZleXJiaW9jJFExKQoKbXlfdHJhbnNwb3NlZF9zdXJ2ZXkgPC0gdChzdXJ2ZXlyYmlvYykKCnN1cnZleXJiaW9jX21vZCA8LSBzdXJ2ZXlyYmlvYwpjb2xuYW1lcyhzdXJ2ZXlyYmlvY19tb2QpIDwtIGMoImFnZSIsImxldmVsIiwicmxldmVsIiwicHJvZ19sZXZlbCIsImdlbm9taWNzX2xldmVsIiwicGFyY29tcF9sZXZlbCIsImV4cGVjdGF0aW9uIikKCnN1cnZleXJiaW9jX21vZCRleHBlY3RhdGlvblt3aGljaC5taW4oc3VydmV5cmJpb2NfbW9kJGFnZSldCmBgYAoKPC9kZXRhaWxzPgoKIyBTdGVwIDM6IEFuYWx5emluZyAodGFidWxhcikgZGF0YQoKRGVzY3JpYmUsIGV4cGxvcmUsIHRyYW5zZm9ybSwgc3VtbWFyaXNlIGRhdGEKCiMjIEV4cGxvcmluZywgc3Vic2V0dGluZywgbWFuaXB1bGF0aW5nLCBhbmFseXNpbmcKCi0gYGRpbSh4KWAgc2hvd3MgdGhlIGRpbWVuc2lvbnMgb2YgYW4gb2JqZWN0Ci0gYHN0cih4KWAgcHJvdmlkZXMgYW4gb3ZlcnZpZXcgb2YgdGhlIHN0cnVjdHVyZSBvZiBhbiBvYmplY3QgYW5kIHRoZSBlbGVtZW50cyBpdCBjb250YWlucwotIGBzdW0oeClgLCBgbWVhbih4KWAsIGBzZCh4KWAgY29tcHV0ZXMgdGhlIHN1bSwgbWVhbiwgb3Igc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGFsbCB0aGUgZWxlbWVudHMgaW4gYHhgOyBgbWVkaWFuKHgpYCwgYHF1YW50aWxlKHgpYAotIGBsZW5ndGgoeClgIHJldHVybnMgdGhlIG51bWJlciBvZiBlbGVtZW50cyBpbiB4IChhIHZlY3RvcikKLSBgc3FydCh4KWAsIGBsb2coeClgIHRha2UgdGhlIHNxdWFyZSByb290IGFuZCB0aGUgbmF0dXJhbCBsb2dhcml0aG0gb2YgYSBudW1lcmljIC0gZWxlbWVudCBvciB2ZWN0b3IKLSBgaGlzdCh4LCBicmVha3M9MjAsIGNvbD0iYmx1ZSIpYCBwbG90cyBhIGhpc3RvZ3JhbSBvZiB2YXJpYWJsZSB4IHdpdGggMjAgYmlucyBjb2xvcmVkIGJsdWUKLSBgdW5pcXVlKHgpYCByZXR1cm5zIHRoZSB2ZWN0b3Igb2YgdW5pcXVlIGVsZW1lbnRzIGluIHgKLSBgcm0oeClgIHJlbW92ZXMgdGhlIG9iamVjdCB4IGZyb20gdGhlIGVudmlyb25tZW50IChgcm0obGlzdD1scygpKWAgcmVtb3ZlcyBhbGwgb2JqZWN0cykKLSBgc2Vzc2lvbkluZm8oKWAgcHJpbnRzIGluZm9ybWF0aW9uIGFib3V0IFIgc2Vzc2lvbiBhbmQgdmVyc2lvbnMgb2YgYWxsIGF0dGFjaGVkIHBhY2thZ2VzCi0gbG9naWNhbCBvcGVyYXRvcnMgbWlnaHQgb2Z0ZW4gY29tZSBoYW5keSEKCgojIyBTdWJzZXR0aW5nIHRoZSBkYXRhCgpUaGlzIGlzIHRoZSBiYXNpYyB3YXkgaXQgd29ya3MKCmBgYHtyIGV2YWw9RkFMU0V9CnN1cnZleXJiaW9jW1JPV1MsQ09MVU1OU10KYGBgCgpZb3UgY2FuIHN1YnNldCB3aXRoLi4uCgotIGludGVnZXJzCi0gYmxhbmsgc3BhY2VzCi0gbmFtZXMKLSBsb2dpY2FsIHZlY3RvcnMKClRyeSB0byBtYWtlIGEgZ3Vlc3MsIGdpdmVuIHRoaXMgdmVjdG9yLgoKYGBge3IgZXZhbD1UUlVFfQp2ZWMgPC0gYyg2LCAxLCAzLCA2LCAxMCwgNSkKYGBgCgpXaGF0IGhhcHBlbnMgaWYgeW91IGRvIHRoaXM/CgpgYGB7cn0KdmVjWzJdCnZlY1tjKDUsIDYpXQp2ZWNbLWMoNSw2KV0KdmVjID4gNQp2ZWNbdmVjID4gNV0KYGBgCgoKV2hhdCBoYXBwZW5zIGlmIHlvdSBkbyB0aGlzPwoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoCiAgbmFtZSA9IGMoIkpvaG4iLCAiUGF1bCIsICJHZW9yZ2UiLCAiUmluZ28iKSwKICBiaXJ0aCA9IGMoMTk0MCwgMTk0MiwgMTk0MywgMTk0MCksCiAgaW5zdHJ1bWVudCA9IGMoImd1aXRhciIsICJiYXNzIiwgImd1aXRhciIsICJkcnVtcyIpCikKCmRmCgpkZltjKDIsIDQpLCAzXQpkZlsgLCAxXQpkZlsgLCAiaW5zdHJ1bWVudCJdCmRmJGluc3RydW1lbnQKYGBgCgoKCkJhY2sgdG8gdGhlIHN1cnZleQoKYGBge3J9CiMgSSBqdXN0IHdhbnQgdGhlIGFnZQpzdXJ2ZXlyYmlvY1ssMV0gCiMgb3IKc3VydmV5cmJpb2MkUTEKCiMgdGhlIGZpcnN0IDQgY29sdW1ucwpzdXJ2ZXlyYmlvY1ssYygxLDIsMyw0KV0Kc3VydmV5cmJpb2NbLDE6NF0KCiMgYWxsIGJ1dCB0aGUgbGFzdCBjb2x1bW4Kc3VydmV5cmJpb2NbLC03XQojIGlmIHlvdSBkb24ndCBrbm93IHdlIGhhZCA3IGNvbHVtbnMuLi4Kc3VydmV5cmJpb2NbLC1uY29sKHN1cnZleXJiaW9jKV0KCiMgeW91IGNhbiBzdWJzZXQgd2l0aCBsb2dpY2FsIHZlY3RvcnMsIGJ5IHJvdyBhbmQgYnkgY29sdW1uCnN1cnZleXJiaW9jW2MocmVwKFRSVUUsMTApLHJlcChGQUxTRSw4KSksXQpzdXJ2ZXlyYmlvY1tjKFRSVUUsRkFMU0UpLF0gIyBrZWVwIGluIG1pbmQgdGhpcyBiZWhhdmlvciEKCiMgZ3Vlc3Mgd2hhdCB0aGlzIGRvZXM/CnN1cnZleXJiaW9jJFEyPT0iUGhEIHN0dWRlbnQiCmBgYAoKCiMjIEV4ZXJjaXNlIHNlc3Npb24gMwoKLSBIb3cgbWFueSBQaEQgc3R1ZGVudHMgZGlkIHJlcGx5PwotIFdoYXQgaXMgdGhlIHByb3BvcnRpb24gb2YgUGhEIHN0dWRlbnRzIHRvIGFsbCBvdGhlciBwYXJ0aWNpcGFudHM/Ci0gSG93IG9sZCBhcmUgdGhleSBvbiBhdmVyYWdlPwotIEhvdyBtYW55IG9mIHRoZSBwYXJ0aWNpcGFudHMgYXJlIG9sZGVyIHRoYW4gMzA/Ci0gSG93IG1hbnkgcG9zdGRvY3MgYXJlIHlvdW5nZXIgdGhhbiAzNT8KLSBIb3cgbWFueSBvZiB0aGUgcGFydGljaXBhbnRzIGRpZCBub3QgcmVwbHkgdG8gdGhlIGxhc3QgcXVlc3Rpb24/IDwhLS0gKGBpcy5uYWAgaXMgeW91ciBmcmllbmQpIC0tPgoKIyMjIEV4ZXJjaXNlIFNlc3Npb24gMyAtIFNvbHV0aW9ucwoKPGRldGFpbHM+CgpgYGB7cn0Kc3VtKHN1cnZleXJiaW9jJFEyID09ICJQaEQgc3R1ZGVudCIpCm1lYW4oc3VydmV5cmJpb2MkUTIgPT0gIlBoRCBzdHVkZW50IikKbWVhbihzdXJ2ZXlyYmlvYyRRMVtzdXJ2ZXlyYmlvYyRRMiA9PSAiUGhEIHN0dWRlbnQiXSkKc3VtKHN1cnZleXJiaW9jJFExID49IDMwKQpzdW0oc3VydmV5cmJpb2MkUTEgPCAzNSAmIHN1cnZleXJiaW9jJFEyID09ICJQb3N0ZG9jIikKc3VtKGlzLm5hKHN1cnZleXJiaW9jJFE3KSkKYGBgCgo8L2RldGFpbHM+CgojIyBNYW5pcHVsYXRpbmcgYW5kIGFuYWx5c2luZyB5b3VyIGRhdGEKCllvdSBjYW4KCi0gc29ydCB0aGUgZGF0YSAoc2VlIGBzb3J0YCBhbmQgYG9yZGVyYCkKLSB0cmFuc2Zvcm0geW91ciBkYXRhOiBhcHBseSBydWxlcyAoZm9ybXVsYXMsIGxvZ2ljcywgaW5zaWdodCBhbHRvZ2V0aGVyKQotIGNvbWJpbmUgdHdvIGRhdGFzZXRzIG9yIG1vcmUgKGlmIHlvdSBgbWVyZ2VgIHRoZW0pCi0gZG8gc29tZSBzdGF0aXN0aWNzIG9uIHlvdXIgZGF0YQoKCiMjIFNvcnRpbmcgdGhlIGRhdGEKCmBgYHtyIGV2YWw9VFJVRX0KbXlvcmQgPC0gb3JkZXIoc3VydmV5cmJpb2MkUTEpCm15b3JkCgpoZWFkKHN1cnZleXJiaW9jW215b3JkLDE6NV0sNCkKc29ydGVkX3N1cnYgPC0gc3VydmV5cmJpb2NbbXlvcmQsMTo2XQpgYGAKCmBzb3J0KClgIHJldHVybnMgeW91IHRoZSBzb3J0ZWQgZGF0YSwgYG9yZGVyKClgIHRoZSBpbmRpY2VzIG9ubHkKCgojIyBUcmFuc2Zvcm1pbmcgdGhlIGRhdGEgCgpgYGB7ciBldmFsPVRSVUV9CiMgdHJhbnNmb3JtaW5nIGEgdmFyaWFibGUKbmV3c3VydmV5IDwtIHN1cnZleXJiaW9jWywxOjVdCm5ld3N1cnZleSRhZ2Vyb290IDwtIHNxcnQobmV3c3VydmV5JFExKQpoZWFkKG5ld3N1cnZleSkKCiMgY3JlYXRpbmcgZ3JvdXBzIG91dCBvZiBhIGNvbnRpbnVvdXMgdmFyaWFibGUKbmV3c3VydmV5JGFnZWdyb3VwIDwtIGN1dChuZXdzdXJ2ZXkkUTEsYnJlYWtzID0gYygyMCwzMCw0MCkpCmhlYWQobmV3c3VydmV5KQpgYGAKClVzZSBjYXNlIGZvciBgbWVyZ2VgOiB5b3UgaGF2ZSAqdHdvKiBzZXRzIHlvdSBhcmUgcGxheWluZyB3aXRoISBUaGluayBpbiBhZHZhbmNlIHdoYXQgeW91IG5lZWQgZm9yIHRoYXQgcHVycG9zZS4uLgoKCiMjIFdlIHdhbnQgc3RhdGlzdGljcyEgCgpBcmUgUGhEIHN0dWRlbnRzICpzaWduaWZpY2FudGx5KiB5b3VuZ2VyIHRoYW4gcG9zdGRvY3M/IEFyZSB0aGVyZSBhbnkgZGlmZmVyZW5jZXMgaW4gdGhlIGFnZSBvZiB0aGUgdGhyZWUgZ3JvdXBzPwoKYGBge3J9CnBoZHMgPC0gc3VydmV5cmJpb2Nbc3VydmV5cmJpb2MkUTI9PSJQaEQgc3R1ZGVudCIsXQpwb3N0ZG9jcyA8LSBzdXJ2ZXlyYmlvY1tzdXJ2ZXlyYmlvYyRRMj09IlBvc3Rkb2MiLF0KdC50ZXN0KHBoZHMkUTEscG9zdGRvY3MkUTEpCmFvdihkYXRhPXN1cnZleXJiaW9jLFExflEyKSAjIFdoYXQgaXMgbWlzc2luZyBoZXJlPwpgYGAKCk11Y2ggbW9yZSBvbiB0aGlzOiBpbiB0aGUgbmV4dCBjb3Vyc2VzIQoKCiMjIFNpbXBsZSB5ZXQgcG93ZXJmdWwgZnVuY3Rpb25zCgpgdGFwcGx5YAoKWW91IHdhbnQgdG8gY2FsY3VsYXRlIHRoZSBtZWRpYW4gYWdlIG9mIGVhY2ggYWNhZGVtaWMgZ3JvdXAgaW4gaGVyZQoKYGBge3IgZXZhbD1UUlVFfQptZCA8LSBtZWRpYW4oc3VydmV5cmJpb2MkUTEpCm1kX21hc3RlciA8LSBtZWRpYW4oc3VydmV5cmJpb2MkUTFbc3VydmV5cmJpb2MkUTI9PSJNYXN0ZXIgc3R1ZGVudC9lbHNlIl0pCm1kX3BoZCA8LSBtZWRpYW4oc3VydmV5cmJpb2MkUTFbc3VydmV5cmJpb2MkUTI9PSJQaEQgc3R1ZGVudCJdKQptZF9wb3N0ZG9jcyA8LSBtZWRpYW4oc3VydmV5cmJpb2MkUTFbc3VydmV5cmJpb2MkUTI9PSJQb3N0ZG9jIl0pCmMobWRfbWFzdGVyLG1kX3BoZCxtZF9wb3N0ZG9jcykKYGBgCgpgdGFwcGx5YCBzcGxpdHMgdGhlIGRhdGEgb2YgdGhlIGZpcnN0IHZhcmlhYmxlIG9uIHRoZSBsZXZlbHMgb2YgdGhlIHNlY29uZCB2YXJpYWJsZSwgYW5kIGFwcGxpZXMgdGhlIGZ1bmN0aW9uICgqYW55KiBmdW5jdGlvbikKCmBgYHtyIGV2YWw9VFJVRX0KdGFwcGx5KFggPSBzdXJ2ZXlyYmlvYyRRMSxJTkRFWCA9IHN1cnZleXJiaW9jJFEyLEZVTiA9IG1lZGlhbikKYGBgCgoKYGxhcHBseWAgYW5kIGBzYXBwbHlgCgpCYWNrIHRvIG91ciBgaXJpc2AgZGF0YXNldAoKYGBge3IgZXZhbD1UUlVFfQpuYW1lcyhpcmlzKQpgYGAKCldlIHdhbnQgdGhlIGF2ZXJhZ2Ugc2VwYWwgbGVuZ3RoIGFuZCB3aWR0aCwgYW5kIHRoZSBzYW1lIGZvciB0aGUgcGV0YWxzLiBVaCwgYW5kIHdlIHdhbnQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiB0b28uCgpgYGB7cn0KIyB0aGUgdW5lZmZpY2llbnQgd2F5OgpzZXBsZW5fbSA8LSBtZWFuKGlyaXMkU2VwYWwuTGVuZ3RoKQpzZXB3aWRfbSA8LSBtZWFuKGlyaXMkU2VwYWwuV2lkdGgpCnBldGxlbl9tIDwtIG1lYW4oaXJpcyRQZXRhbC5MZW5ndGgpCnBldHdpZF9tIDwtIG1lYW4oaXJpcyRQZXRhbC5XaWR0aCkKCnNlcGxlbl9tIDwtIHNkKGlyaXMkU2VwYWwuTGVuZ3RoKQojIC4uLiBhbmQgc28gb24KYGBgCgotPiBBcHBseSBhIEZ1bmN0aW9uIG92ZXIgYSBMaXN0IG9yIFZlY3RvcgoKYGBge3J9CiMgd2Ugd2lsbCB1c2UganVzdCB0aGUgZmlyc3QgZm91ciBjb2x1bW5zCmxhcHBseShpcmlzWywxOjRdLG1lYW4pCnNhcHBseShpcmlzWywxOjRdLG1lYW4pCmxhcHBseShpcmlzWywxOjRdLHNkKQojIC4uLgpgYGAKClRoZSBtYWpvciBkaWZmZXJlbmNlIGlzIGluIHRoZSBwcmVzZW50YXRpb24gb2YgdGhlIG91dHB1dAoKCmBzdW1tYXJ5YAoKVHJ5IG91dCBgc3VtbWFyeWAgb24gYSBgZGF0YS5mcmFtZWAKCmBgYHtyfQpzdW1tYXJ5KGlyaXMpCmBgYAoKQWx0ZXJuYXRpdmVzIGluIG90aGVyIHBhY2thZ2VzOgoKLSBgZGVzY3JpYmUoKWAgaW4gdGhlIGBIbWlzY2AgcGFja2FnZQotIGBza2ltKClgIGZyb20gYHNraW1yYAotIGBjcmVhdGVfcmVwb3J0KClgIGZyb20gYERhdGEgRXhwbG9yZXJgCgoKYHRhYmxlYAoKYGBge3J9CnRhYmxlKHN1cnZleXJiaW9jJFEzKQp0YWJsZShzdXJ2ZXlyYmlvYyRRNCkKCnRhYmxlKHN1cnZleXJiaW9jJFEyLHN1cnZleXJiaW9jJFEzKQpgYGAKCi0gd2FudCB0aGUgc3Vtcz8gVHJ5IGBhZGRtYXJnaW5zKClgCi0gbG9va2luZyBmb3IgdGhlIHBlcmNlbnRhZ2UgdmFsdWVzPyBgcHJvcC50YWJsZSgpYAotIHNvbWV3aGF0IG5pY2VyIG91dHB1dDogYGZ0YWJsZSgpYAoKYGBge3J9CmFkZG1hcmdpbnModGFibGUoc3VydmV5cmJpb2MkUTIsc3VydmV5cmJpb2MkUTMpKQpwcm9wLnRhYmxlKHRhYmxlKHN1cnZleXJiaW9jJFEyLHN1cnZleXJiaW9jJFEzKSkKYGBgCgoKUGxlYXNlIGFsd2F5cyBkbyBjaGVjayB0aGUgZG9jcyEKCgoKIyMgRXhlcmNpc2Ugc2Vzc2lvbiA0CgpUaGUgYE1BU1NgIHBhY2thZ2UgY29udGFpbnMgdGhlIGRhdGFzZXQgYENhcnM5M2AsIHdoaWNoIHN0b3JlcyB0aGUgZGF0YSBvbiA5MyBtYWtlcyBvZiBjYXIgc29sZCBpbiBVUwoKLSB5b3UnbGwgbmVlZCB0aGUgcGFja2FnZSAqYW5kKiB0aGUgZGF0YQotIGBUeXBlYCBzcGVjaWZpZXMgdGhlIHR5cGUgb2YgbWFya2V0IHRoZSBjYXIgaXMgYWltZWQgYXQuIEZpbmQgdGhlIGNoZWFwZXN0IGNhciBpbiBlYWNoIHR5cGUsIGFuZCB0aGUgb25lIHdpdGggdGhlIGdyZWF0ZXN0IGZ1ZWwgZWZmaWNpZW5jeQotIGNvbXB1dGUgdGhlIG1lYW4gaG9yc2Vwb3dlciBmb3IgZWFjaCB0eXBlCi0gY3JlYXRlIHR3byBgZGF0YS5mcmFtZWBzLCBvbmUgZm9yIFVTIGNhcnMsIHRoZSBvdGhlciBvbmUgd2l0aCBub24tVVMgY2FycwotIGV4cG9ydCB0aGUgVVMgY2FycyB0byBhIHRleHQgZmlsZQotIHNhdmUgdGhlIG5vbi1VUyBjYXJzIGRhdGEgdG8gYSBiaW5hcnkgZmlsZSAoYC5SRGF0YWApCgojIyMgRXhlcmNpc2UgU2Vzc2lvbiA0IC0gU29sdXRpb25zCgo8ZGV0YWlscz4KCmBgYHtyfQpsaWJyYXJ5KE1BU1MpCmhlYWQoQ2FyczkzKQo/Q2FyczkzCnRhcHBseShYID0gQ2FyczkzJE1pbi5QcmljZSxJTkRFWCA9IENhcnM5MyRUeXBlLEZVTiA9IG1pbikKdGFwcGx5KFggPSBDYXJzOTMkSG9yc2Vwb3dlcixJTkRFWCA9IENhcnM5MyRUeXBlLEZVTiA9IG1lYW4pCgp0YWJsZShDYXJzOTMkT3JpZ2luKQp1c19jYXJzIDwtIENhcnM5M1tDYXJzOTMkT3JpZ2luID09ICJVU0EiLF0Kbm9udXNfY2FycyA8LSBDYXJzOTNbQ2FyczkzJE9yaWdpbiAhPSAiVVNBIixdCiMgd3JpdGUuY3N2KHVzX2NhcnMsIGZpbGUgPSAidXNfY2Fycy5jc3YiKQojIHNhdmUobm9udXNfY2FycywgZmlsZSA9ICJub251c19jYXJzLlJEYXRhIikKYGBgCgo8L2RldGFpbHM+CgojIFN0ZXAgNDogUGxvdHRpbmcgZGF0YQoKIyMgR3JhcGhpY3MgaW4gUgoKIC0gcG93ZXJmdWwgZW52aXJvbm1lbnQgZm9yIHZpc3VhbGl6aW5nIHNjaWVudGlmaWMgZGF0YQogLSBpbnRlZ3JhdGVkIGdyYXBoaWNzICoqQU5EKiogc3RhdGlzdGljcwogLSBwdWJsaWNhdGlvbi1yZWFkeSBxdWFsaXR5CiAtIGZ1bGx5IHByb2dyYW1tYWJsZSwgaGlnaGx5IHJlcHJvZHVjaWJsZQoKTWFueSB3YXlzIGZvciB0aGUgc2FtZSB0YXNrOgoKLSBiYXNlIGdyYXBoaWNzIChgcGxvdGApCi0gYGdncGxvdDJgCi0gYGxhdHRpY2VgCi0gaW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbnMgc3VjaCBhcyBgcGxvdGx5YCwgYGdndmlzYCBvciBvdGhlciBsaWJyYXJpZXMKCldoeSBib3RoZXIgcGxvdHRpbmcgYXQgYWxsPwoKLSBmYWNpbGl0YXRlIGNvbXBhcmlzb25zCi0gaWRlbnRpZnkgdHJlbmRzCi0gZ2VuZXJhdGUgaHlwb3RoZXNlcwoKCgojIyBZb3UgY2FuIGRvIGFsbCB0aGlzIHdpdGggUgoKTG9vayBmb3Igc29tZSBpbnNwaXJhdGlvbiBoZXJlLCBpdCBpcyBhbiBleGNlbGxlbnQgcGxhY2UgdG8gc3RhcnQgYW5kIGxlYXJuIQoKaHR0cHM6Ly9yLWdyYXBoLWdhbGxlcnkuY29tLwoKIyMgVGhlIGBwbG90YCBmdW5jdGlvbgoKRmlyc3QgdGhpbmc6IHRha2UgYSBsb29rIGF0IHRoZSBvdmVydmlldyBkb2N1bWVudGF0aW9uIG9mIGBwbG90YAoKYGBge3J9Cj9wbG90CmBgYAoKV2Ugd2lsbCBzZWUKCi0gc2NhdHRlciBwbG90cwotIGJveHBsb3RzCi0gYmFycGxvdHMKLSBoaXN0b2dyYW1zCgoKIyMgYHBsb3RgIHBhcmFtZXRlcnMgCgpSZXF1aXJlZDoKCi0geCB2YXJpYWJsZQotIHkgdmFyaWFibGUKCk90aGVyIG9wdGlvbnMKCi0gdGl0bGUgd2l0aCBgbWFpbmAKLSBheGVzIGxhYmVscyB3aXRoIGB4bGFiYCBhbmQgYHlsYWJgCi0gYXhlcyBsaW1pdHMgd2l0aCBgeGxpbWAgYW5kIGB5bGltYAotIHN5bWJvbHMsIGNvbG9ycyBhbmQgc2l6ZXM6IGBwY2hgLCBgY29sYCBhbmQgYGNleGAgLSBhcyBhdG9taWMgZWxlbWVudHMgb3IgYXMgdmVjdG9ycwoKCiMjIEdldCB0byBrbm93IHRoZSBkYXRhOiBgbXBnYAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikgIyB0aGlzIGlzIHVzZWZ1bCBwZXIgc2UsIGFuZCBjb250YWlucyB0aGUgZGF0YXNldCB3ZSB3aWxsIGJlIHVzaW5nCj9tcGcKYGBgCgogICAgVGhpcyBkYXRhc2V0IGNvbnRhaW5zIGEgc3Vic2V0IG9mIHRoZSBmdWVsIGVjb25vbXkgZGF0YSB0aGF0IHRoZSBFUEEgbWFrZXMgYXZhaWxhYmxlIG9uIGh0dHA6Ly9mdWVsZWNvbm9teS5nb3YKCmBgYHtyfQojIHdvcmtzIG9uIFJTdHVkaW8KIyBWaWV3KG1wZykKIyBvdGhlcndpc2Ugc3RpY2sgdG8gdGhlIGNsYXNzaWMKc3RyKG1wZykKYGBgCgpNYWtlIGEgZ3Vlc3M6IHdoYXQgZG8geW91IGV4cGVjdCB0byBzZWUgYmV0d2VlbiBmdWVsIGNvbnN1bXB0aW9uIGFuZCBlbmdpbmUgc2l6ZT8KCiMjIFNjYXR0ZXIgcGxvdHMKCmBgYHtyIGV2YWw9VFJVRX0KcGxvdChtcGckZGlzcGwsbXBnJGN0eSkKYGBgCgpCb251czogd2hhdCBpcyB0aGUgYGNvcmByZWxhdGlvbj8KCmBgYHtyfQpjb3IobXBnJGRpc3BsLG1wZyRjdHkpCmNvcihtcGckZGlzcGwsbXBnJGN0eSxtZXRob2Q9InNwZWFybWFuIikKYGBgCgoKIyMjIENhbiB3ZSBkbyBtb3JlPwoKYGBge3IgZXZhbD1UUlVFfQptcGckbXlncm91cCA8LSBhcy5udW1lcmljKGZhY3RvcihtcGckY2xhc3MpKQpwbG90KG1wZyRkaXNwbCxtcGckY3R5LAogICAgIGNvbCA9IG1wZyRteWdyb3VwKQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQgPSBsZXZlbHMoZmFjdG9yKG1wZyRjbGFzcykpLGNvbD1sZXZlbHMoZmFjdG9yKG1wZyRteWdyb3VwKSkscGNoPTEpCmBgYAoKCmBgYHtyIGV2YWw9VFJVRX0KcGxvdChtcGckZGlzcGwsbXBnJGN0eSwKICAgICBwY2ggPSBhcy5udW1lcmljKGZhY3RvcigobXBnJGNsYXNzKSkpKQpgYGAKClRoaXMgc2hvd3Mgd2UgaGF2ZSBxdWl0ZSBzb21lIG92ZXJsYXAgb2YgcG9pbnRzLiBXaGF0IGNhbiB3ZSBkbz8KCkFkZGluZyBzb21lIGppdHRlci4uLgoKYGBge3IgZXZhbD1UUlVFfQpwbG90KHggPSBtcGckZGlzcGwgKyBybm9ybShucm93KG1wZyksbWVhbiA9IDAsc2QgPSAwLjAxKSwKICAgICB5ID0gbXBnJGN0eSArIHJub3JtKHJub3JtKG5yb3cobXBnKSxtZWFuID0gMCxzZCA9IDAuMDEpKSwKICAgICBjb2wgPSBtcGckbXlncm91cCwKICAgICBtYWluID0gIm5vdyB3aXRoIGppdHRlciEiKQpgYGAKCgpBZGRpbmcgYSBzbW9vdGhpbmcgbGluZQoKVHJ5aW5nIHRvIHNlZSBhIHBhdHRlcm4/IEFkZCBhIHNtb290aGluZyBjdXJ2ZS4KClRoaXMgb25lIGlzIHdyb25nIC0gbWlzc2luZyB0aGUgcmVvcmRlcmluZyBvZiBwb2ludHMKCmBgYHtyIGV2YWw9VFJVRX0KcGxvdChtcGckZGlzcGwsbXBnJGN0eSwgY29sID0gbXBnJG15Z3JvdXApCm15bG9lc3MgPC0gbG9lc3MoY3R5fmRpc3BsLCBkYXRhPW1wZykKbXlmaXQgPC0gZml0dGVkKG15bG9lc3MpCmxpbmVzKG1wZyRkaXNwbCxteWZpdCkKbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kID0gbGV2ZWxzKGZhY3RvcihtcGckY2xhc3MpKSxjb2w9bGV2ZWxzKGZhY3RvcihtcGckbXlncm91cCkpLHBjaD0xKQpgYGAKClRoaXMgb25lIGlzIGNvcnJlY3QhCgpgYGB7ciBldmFsPVRSVUV9CnBsb3QobXBnJGRpc3BsLG1wZyRjdHksIGNvbCA9IG1wZyRteWdyb3VwKQpteWxvZXNzIDwtIGxvZXNzKGN0eX5kaXNwbCwgZGF0YT1tcGcpCm15Zml0IDwtIGZpdHRlZChteWxvZXNzKQpteW9yZCA8LSBvcmRlcihtcGckZGlzcGwpCmxpbmVzKG1wZyRkaXNwbFtteW9yZF0sbXlmaXRbbXlvcmRdKQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQgPSBsZXZlbHMoZmFjdG9yKG1wZyRjbGFzcykpLGNvbD1sZXZlbHMoZmFjdG9yKG1wZyRteWdyb3VwKSkscGNoPTEpCmBgYAoKYGxpbmVzYCBjYW4gYWRkIChhbG1vc3QpIGFueXRoaW5nIChhbnkgbGluZSkuIAoKYHBvaW50c2Agd29ya3MgaW4gYSBzaW1pbGFyIHdheSB0byBzdXBlcmltcG9zZSwgd2VsbCwgcG9pbnRzCgoKIyMgQmFyIGNoYXJ0cwoKYGBge3J9Cj9iYXJwbG90CmBgYAoKYGBge3IgZXZhbD1UUlVFfQphY2FkZW1pYV9sZXZlbHMgPC0gdGFibGUoc3VydmV5cmJpb2MkUTIpCmJhcnBsb3QoYWNhZGVtaWFfbGV2ZWxzKQpgYGAKCgojIyBCb3hwbG90cwoKSG93IGlzIHRoZSBhZ2UgZGlzdHJpYnV0ZWQgYWNyb3NzIGFjYWRlbWljIGxldmVscz8gQ2hlY2sgdGhlIGhlbHAgb2YgYGJveHBsb3RgCgotIEEgZm9ybXVsYSBpcyByZXF1aXJlZCEKLSBEb24ndCB3b3JyeSwgaXQncyBub3RoaW5nIGJ1dCB5b3VyIGB5fnhgIHZhcmlhYmxlcyAtIG9rLCBpdCBjYW4gZ2V0IG1vcmUgY29tcGxpY2F0ZWQKICAgIApgYGB7ciBldmFsPVRSVUV9CmJveHBsb3QoUTF+UTIsCiAgICAgICAgZGF0YSA9IHN1cnZleXJiaW9jKQpgYGAKClNwbGl0dGluZyBvbiBtb3JlIGZhY3RvcnMKCmBgYHtyIGV2YWwgPSBUUlVFfQpib3hwbG90KFExflEyK1EzLAogICAgICAgIGRhdGEgPSBzdXJ2ZXlyYmlvYykKYGBgCgpNYWtpbmcgaXQgbW9yZSByZWFkYWJsZS4uLgoKYGBge3IgZXZhbCA9IFRSVUV9CmJveHBsb3QoUTF+UTIrUTMsCiAgICAgICAgZGF0YSA9IHN1cnZleXJiaW9jLAogICAgICAgIGxhcyA9IDIpCmBgYAoKQ2hhbmdpbmcgdGhlIGBwYXJgYW1ldGVycyBhbGxvd3MgeW91IHRvIGNvbnRyb2wgbWFueSBhc3BlY3RzIG9uIHBsb3QgYXBwZWFyYW5jZQpgcGFyYCBpcyB5b3VyIGJlc3QgZnJpZW5kIC0gYW5kIGVuZW15IChzZWUgYD9wYXJgKQoKYGBge3IgZXZhbD1UUlVFfQpwYXIobWFyPWMoMTUsMywyLDIpKQpib3hwbG90KFExflEyK1EzLGRhdGEgPSBzdXJ2ZXlyYmlvYyxsYXMgPSAyKQpgYGAKCmBwYXIoIC4uLiApYCBoYXMgbWFueSBhcmd1bWVudHM7IGhlcmUsIHRoZSB1c2VmdWwvbW9zdCB1c2VkIG9uZXMKCi0gYG1hcmAgZm9yIGhhbmRsaW5nIHRoZSBtYXJnaW5zCi0gYGNleGAsIGBjb2xgLCBgcGNoYCBhbmQgY28uIGFyZSBhbGwgcGFyYW1ldGVycyBvZiBgcGFyYAotIGBsYXNgIHRvIGNoYW5nZSB0aGUgc3R5bGUgb2YgdGhlIGF4aXMgbGFiZWxzCi0gYG1mcm93YCB0byBkcmF3IGFuIGFycmF5IG9mIGZpZ3VyZXMKCgojIyBIaXN0b2dyYW1zCgpgYGB7ciBldmFsPVRSVUV9Cmhpc3Qoc3VydmV5cmJpb2MkUTEsYnJlYWtzID0gOCkKYGBgCgoKIyMgTW9yZSBoaXN0b2dyYW1zIQoKYGBge3J9Cmhpc3QobXBnJGN0eSxicmVha3MgPSAxMCkKaGlzdChtcGckY3R5LGJyZWFrcyA9IDEwLCBjb2wgPSAic3RlZWxibHVlIikKaGlzdChtcGckY3R5LGJyZWFrcyA9IDEwLCBjb2wgPSAic3RlZWxibHVlIiwgYm9yZGVyID0gImdyYXkiKQpoaXN0KG1wZyRjdHksYnJlYWtzID0gMTAsIGNvbCA9ICJzdGVlbGJsdWUiLCBib3JkZXIgPSAiZ3JheSIsbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgbWlsZXMvZ2FsbG9uIGNvbnN1bXB0aW9uIGluIGNpdHkgdHJhZmZpYyIpCmBgYAoKCiMjIEhvdyB0byBkbyBuaWNlIHBpZSBjaGFydHMKCkRPTidULgoKSWYgeW91ICoqcmVhbGx5KiogbmVlZCB0byBkbyBpdC4uLgoKYGBge3J9Cj9waWUKZXhhbXBsZShwaWUpICMgZXhwZWNpYWxseSB0aGUgbGFzdCBvbmUKYGBgCgpgYGB7cn0KcGllKGMoMjAsIDgwKSwgaW5pdC5hbmdsZT0tNDAsCiAgICBjb2w9Yygid2hpdGUiLCAieWVsbG93IiksIAogICAgbGFiZWw9Yygibm8gcGFjbWFuIiwgInBhY21hbiIpLAogICAgYm9yZGVyID0gImxpZ2h0Z3JleSIpCmBgYAoKLi4uIG9yIHN3aXRjaCBmcm9tIHBpZSB0byB3YWZmbGUgKHNlcmlvdXNseSkKCiMjIEhvdyB0byBkbyAzRCBleHBsb2RlZCBwaWUgY2hhcnRzCgoqKkRPTidUKiouIEFuZCB0aGlzIHRpbWUgSSBtZWFuIGl0Cgoqc2FkbHkgZW5vdWdoIHRoZXJlIHdvdWxkIGJlIHBhY2thZ2VzIGZvciB0aGlzLCB0b28qCgoKCiMjIEV4dHJhOiAqZHluYW1pdGUqIHBsb3RzCgphLmsuYS4gV2h5IGlzIHRoaXMgYmFkPwoKYGBge3IgZXZhbD1UUlVFfQphZ2VfYnlfZ3JvdXAgPC0gdGFwcGx5KHN1cnZleXJiaW9jJFExLHN1cnZleXJiaW9jJFEyLG1lYW4pCnNkX2J5X2dyb3VwIDwtIHRhcHBseShzdXJ2ZXlyYmlvYyRRMSxzdXJ2ZXlyYmlvYyRRMixzZCkKbXliYXIgPC0gYmFycGxvdChhZ2VfYnlfZ3JvdXAsY29sPWMoImtoYWtpIiwic2FsbW9uIiwiZmlyZWJyaWNrIiksIHlsaW09YygwLG1heChhZ2VfYnlfZ3JvdXApICsgNSkpCiMgbXliYXIsIGluc3BlY3QgaXQKYXJyb3dzKG15YmFyLCBhZ2VfYnlfZ3JvdXAsbXliYXIsIChhZ2VfYnlfZ3JvdXAgKyBzZF9ieV9ncm91cCksIGxlbmd0aCA9IDAuMTUsYW5nbGU9IDkwKQpgYGAKCgpEeW5hbWl0ZSBwbG90cyBWUyBib3hwbG90cwoKYGBge3IgZXZhbD1UUlVFfQpib3hwbG90KFExflEyLAogICAgICAgIGRhdGEgPSBzdXJ2ZXlyYmlvYykKYGBgCgpNZWRpYW4gVlMgZGlzdHJpYnV0aW9uIFZTIGFjdHVhbCBwb2ludHMuLi4gV2hhdCBkbyB5b3UgcmVhbGx5IHdhbnQgdG8gc2hvdz8KCgojIyBXaGF0IGNhbiB5b3UgZG8gbW9yZSB3aXRoIHlvdXIgcGxvdD8KCi0gY2hhbmdlIHRoZSBwb2ludHMgdHlwZSAtIHNlZSBgdHlwZWAgaW4gYD9wbG90YAotIHVzZSBsb2cgc2NhbGVzIC0gc2VlIGBsb2dgCi0gYW5ub3RhdGUgKHNvbWUgb2YpIHRoZSBwb2ludHMgLSB3aXRoIGB0ZXh0YAotIGNoYW5nZSBmb250IHNpemVzLCBzdHlsZXMgYW5kIHNvIG9uCi0gdXNlIHNwZWNpYWwgY2hhcmFjdGVycyB3aXRoIGBleHByZXNzaW9uYAotIHNhdmUgdGhlIHBsb3QKICAtIHVzZSB0aGUgcG9pbnQtYW5kLWNsaWNrIGludGVyZmFjZSBpbiBSU3R1ZGlvCiAgLSBjb2RlIGl0CiAgICAKIyMjIFNhdmluZyB5b3VyIHBsb3RzCgpHZW5lcmFsIGNvZGUgc3RydWN0dXJlIGZvciB0aGlzCgpgYGAKb3BlbmRldmljZSgpCi4uLgpjb2RlIGZvciB0aGUgcGxvdAouLi4KY2xvc2VkZXZpY2UoKQpgYGAKCmBgYHtyIGV2YWw9RkFMU0V9CnBkZigibXlmaWxlbmFtZS5wZGYiKQojIHNlZSBhbHNvIGFsdGVybmF0aXZlczoKIyMgcG5nKCkKIyMganBlZygpCnBsb3QobXBnJGRpc3BsLG1wZyRjdHksCiAgICAgY29sID0gbXBnJG15Z3JvdXApCmRldi5vZmYoKQpgYGAKCgoKIyMgUGV0YWxzIGFuZCBzZXBhbHMKCjxpbWcgc3JjPSJpbWFnZXMvcGV0YWwtc2VwYWwuanBnIiBhbHQ9IiIgaGVpZ2h0PSI2MDAiLz4KCiMjIEV4ZXJjaXNlIHNlc3Npb24gNQoKCkJhY2sgdG8gdGhlIGBpcmlzYC4gVGhyZWUgc3BlY2llcyBhcmUgdGhlcmUuIEV4cGxvcmUgdGhlIGRhdGFzZXQgaW4gdGhlIGZvbGxvd2luZyB3YXlzOgoKLSBkcmF3IGEgaGlzdG9ncmFtIG9mIHRoZSBwZXRhbCBsZW5ndGguIFdoYXQgZG8geW91IHNlZT8KLSBwbG90IHNlcGFsIGxlbmd0aCB2ZXJzdXMgcGV0YWwgbGVuZ3RoLiBBZGQgZGlmZmVyZW50IGNvbG9ycyB0byBoaWdobGlnaHQgdGhlIHNwZWNpZXMKLSBkbyB0aGUgc2FtZSBmb3Igc2VwYWwgd2lkdGggYW5kIHNlcGFsIGxlbmd0aCwgYW5kIHRoaXMgdGltZSB1c2UgYSBkaWZmZXJlbnQgc3ltYm9sIGZvciB0aGUgc3BlY2llcy4gQWRkIGEgbGVnZW5kIGFuZCBhIHRpdGxlIGlmIHlvdSB3YW50Ci0gKGhhcmRlcikgY2FsY3VsYXRlIHRoZSBtZWFuIHZhbHVlcyBvZiBlYWNoIGZlYXR1cmUgZm9yIGVhY2ggc3BlY2llcywgb3JnYW5pemluZyBpdCBpbiBhIG1hdHJpeCB3aGVyZSB0aGUgcm93cyBhcmUgdGhlIHNwZWNpZXMgbmFtZXMuIEdlbmVyYXRlIGEgc3RhY2tlZCBiYXIgcGxvdCB3aXRoIGl0LCBhbmQgYW5vdGhlciBvbmUgd2hlcmUgdGhlIGJhcnMgYXJlIGFycmFuZ2VkIGhvcml6b250YWxseQoKLSBmZWVsIGZyZWUgdG8gZ28gYmFjayB0byB0aGUgc3VydmV5IGRhdGEgYW5kIGV4cGxvcmUgaXQgZnVydGhlciEKCiMjIyBFeGVyY2lzZSBTZXNzaW9uIDUgLSBTb2x1dGlvbnMKCjxkZXRhaWxzPgoKYGBge3J9Cmhpc3QoaXJpcyRQZXRhbC5MZW5ndGgpCnBsb3QoaXJpcyRTZXBhbC5MZW5ndGgsaXJpcyRQZXRhbC5MZW5ndGgpCnBsb3QoaXJpcyRTZXBhbC5MZW5ndGgsaXJpcyRQZXRhbC5MZW5ndGgsY29sPWlyaXMkU3BlY2llcykKcGxvdChpcmlzJFNlcGFsLldpZHRoLGlyaXMkU2VwYWwuTGVuZ3RoLCBwY2ggPSBhcy5udW1lcmljKGZhY3RvcihpcmlzJFNwZWNpZXMpKSkKbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kID0gbGV2ZWxzKGZhY3RvcihpcmlzJFNwZWNpZXMpKSxwY2g9dW5pcXVlKGZhY3RvcihpcmlzJFNwZWNpZXMpKSkKCnNsX21lYW5zIDwtIHRhcHBseShpcmlzJFNlcGFsLkxlbmd0aCxpcmlzJFNwZWNpZXMsbWVhbikKcGxfbWVhbnMgPC0gdGFwcGx5KGlyaXMkUGV0YWwuTGVuZ3RoLGlyaXMkU3BlY2llcyxtZWFuKQpzd19tZWFucyA8LSB0YXBwbHkoaXJpcyRTZXBhbC5XaWR0aCxpcmlzJFNwZWNpZXMsbWVhbikKcHdfbWVhbnMgPC0gdGFwcGx5KGlyaXMkUGV0YWwuV2lkdGgsaXJpcyRTcGVjaWVzLG1lYW4pCm15bWF0IDwtIGNiaW5kKHNsX21lYW5zLHBsX21lYW5zLHN3X21lYW5zLHB3X21lYW5zKQpiYXJwbG90KG15bWF0LGxlZ2VuZC50ZXh0ID0gdW5pcXVlKGlyaXMkU3BlY2llcykpCmJhcnBsb3QobXltYXQsYmVzaWRlID0gVFJVRSxsZWdlbmQudGV4dCA9IHVuaXF1ZShpcmlzJFNwZWNpZXMpKQpgYGAKCjwvZGV0YWlscz4KCiMjIFNvbWV0aGluZyBjb29sIHRvIGhhdmUgYW4gb3ZlcnZpZXcuLi4KCmBgYHtyIGV2YWw9VFJVRX0KcGFpcnMoaXJpc1ssMTo0XSxjb2w9aXJpcyRTcGVjaWVzKQpgYGAKCllvdSBjYW4gdXNlIHRoZSBwYW5lbHMgZXZlbiBtb3JlIGNsZXZlcmx5LCBjaGVjayB0aGUgaGVscCBvZiBgcGFpcnNgIQoKVGhpcyBpcyBhIGNvbGxlY3Rpb24gb24gZ3JhcGhzIGluIFIgLSB3aXRoIHRoZSB1bmRlcmx5aW5nIGNvZGUgdG9vLgoKaHR0cDovL3NoaW55LnN0YXQudWJjLmNhL3ItZ3JhcGgtY2F0YWxvZy8KCgojIyBUaGUgYGdhcG1pbmRlcmAgcHJvamVjdAoKaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1oVmltVnpndEQ2dwoKPCEtLSA8aWZyYW1lIHdpZHRoPSI4NTQiIGhlaWdodD0iNDgwIiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL2hWaW1Wemd0RDZ3P3Q9MjUiIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYXV0b3BsYXk7IGVuY3J5cHRlZC1tZWRpYSIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPiAtLT4KCiMjIE1lZXQgYGdncGxvdDJgCgpCdXQgZmlyc3QsIG1lZXQgdGhlIGBnYXBtaW5kZXJgIGRhdGEgCgpgYGB7cn0KbGlicmFyeShnYXBtaW5kZXIpCmhlYWQoZ2FwbWluZGVyKQpoZWFkKGNvdW50cnlfY29sb3JzKQpoZWFkKGNvbnRpbmVudF9jb2xvcnMpCmBgYAoKVmFyaWFibGVzOiAKCi0gY291bnRyeSAJCi0gY29udGluZW50IAkKLSB5ZWFyIAkKLSBsaWZlRXhwLCBsaWZlIGV4cGVjdGFuY3kgYXQgYmlydGgKLSBwb3AsIHRvdGFsIHBvcHVsYXRpb24gCi0gZ2RwUGVyY2FwLCBwZXItY2FwaXRhIEdEUAoKPCEtLSA8aW1nIHNyYz0iaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2plbm55YmMvZ2FwbWluZGVyL21hc3Rlci9kYXRhLXJhdy9nYXBtaW5kZXItY29sb3Itc2NoZW1lLWdncGxvdDIucG5nIiBhbHQ9IiIgaGVpZ2h0PSI0MDAiLz4gLS0+Cgo8IS0tICFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vamVubnliYy9nYXBtaW5kZXIvbWFzdGVyL2RhdGEtcmF3L2dhcG1pbmRlci1jb2xvci1zY2hlbWUtZ2dwbG90Mi5wbmcpIC0tPgoKCiMjIFRoZSBgZ2dwbG90MmAgcGhpbG9zb3BoeQoKYGdnYCBzdGFuZHMgZm9yIHRoZSBncmFtbWFyIG9mIGdyYXBoaWNzCgotIHlvdSBwcm92aWRlIHRoZSBgZGF0YWAKLSB5b3UgbWFwIHRoZSBkYXRhIHRvIGBhZXNgdGhldGljcyAoc2hhcGUsIHNpemUsIGNvbG91cikKLSB5b3UgYWRkIGBnZW9tYHMgdG8gc3BlY2lmeSBob3cgeW91IHdhbnQgdG8gaGF2ZSB0aGUgZGF0YSBwbG90dGVkCi0geW91IGNhbiBoYXZlIGBzdGF0YGlzdGljYWwgdHJhbnNmb3JtYXRpb25zCi0gYGZhY2V0YHMgYWxsb3cgeW91IHRvIGRvIHF1aWNrIGVsZWdhbnQgbXVsdGkgcGxvdHMKCkl0IGNhbiBjb21lIGFjcm9zcyBzb21ld2hhdCBoYXJkZXIgc2luY2UKCi0gZGF0YSBuZWVkIHRvIGJlIHRpZHkgLSBvbmUgb2JzZXJ2YXRpb24gcGVyIHJvdwotIHJlcXVpcmVzIGFuIGV4dHJhIHN0ZXAgZm9yIGFic3RyYWN0aW9uCgp5ZXQsIGl0IG1ha2VzIHRoZSB3aG9sZSBwcm9jZXNzIG9mICJ0aGlua2luZyBkYXRhIiBtb3JlIG5hdHVyYWwuCgoKIyMjIEEgcXVpY2sgZGl2ZSBpbnRvIHRoZSBtYW55IG9wdGlvbnMKCgpgYGB7ciBldmFsID0gVFJVRX0KZ2dwbG90KGdhcG1pbmRlcixhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KZ2dwbG90KGdhcG1pbmRlcixhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKSArIGdlb21fcG9pbnQoKQpgYGAKCldlIGNhbiBzdG9yZSBgZ2dwbG90YCBwbG90IG9iamVjdHMgaW50byBhIHZhcmlhYmxlIC0gYW5kIGJ1aWxkIHVwb24gdGhhdCBsYXRlciAKCmBgYHtyIGV2YWw9VFJVRX0KcCA8LSBnZ3Bsb3QoZ2FwbWluZGVyLGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCkpIApwICsgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeF9sb2cxMCgpCnAgPC0gcCArIHNjYWxlX3hfbG9nMTAoKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fcG9pbnQoY29sb3I9ImJsdWUiKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fcG9pbnQoY29sb3I9InN0ZWVsYmx1ZSIsIHBjaD0xOSwgc2l6ZT04LCBhbHBoYT0xLzQpCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9wb2ludChhZXMoY29sb3I9Y29udGluZW50KSkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KGFlcyhjb2w9Y29udGluZW50KSwgc2l6ZT00KQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fcG9pbnQoYWVzKGNvbD1jb250aW5lbnQsIHNpemU9cG9wKSkgCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9wb2ludChhZXMoY29sPWNvbnRpbmVudCwgc2l6ZT1wb3ApKSArIGdlb21fc21vb3RoKCkKYGBgCgpgYGB7ciBldmFsPVRSVUV9Cm5pY2VvbmUgPC0gcCArIGdlb21fcG9pbnQoYWVzKGNvbD1jb250aW5lbnQsIHNpemU9cG9wKSkgKyAKICBnZW9tX3Ntb290aChhZXMoY29sPWNvbnRpbmVudCksc2U9RkFMU0UpCm5pY2VvbmUKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KGFlcyhjb2w9Y29udGluZW50LCBzaXplPXBvcCkpICsgCiAgZ2VvbV9zbW9vdGgobHdkPTIsIHNlPUZBTFNFLCBtZXRob2Q9ImxtIiwgY29sPSJyZWQiKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fcG9pbnQoYWVzKGNvbD1jb250aW5lbnQsIHNpemU9cG9wKSkgKyAKICBnZW9tX3Ntb290aChhZXMoY29sPWNvbnRpbmVudCksbHdkPTIsIHNlPUZBTFNFLCBtZXRob2Q9ImxtIikKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5jb250aW5lbnQpIApgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fcG9pbnQoYWVzKGNvbD1jb250aW5lbnQpKSArIGZhY2V0X3dyYXAofmNvbnRpbmVudCkgCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9wb2ludChhZXMoY29sPWNvbnRpbmVudCkpICsgZ2VvbV9zbW9vdGgoKSArIGZhY2V0X3dyYXAofmNvbnRpbmVudCkKYGBgCgojIyMgU2F2aW5nIHRoZSBwbG90cwoKYGBge3IgZXZhbD1GQUxTRX0KZ2dzYXZlKGZpbGU9Im15cGxvdC5wbmciKQpgYGAKCiMjIyBMaW5lIHBsb3RzCgpgYGB7ciBldmFsPVRSVUV9CmdncGxvdChnYXBtaW5kZXIsCiAgICAgICBhZXMoeCA9IHllYXIsIHkgPSBsaWZlRXhwLCBncm91cCA9IGNvdW50cnksIGNvbG9yID0gY291bnRyeSkKICAgICAgICkgKwogIGdlb21fbGluZShsd2QgPSAxLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvdW50cnlfY29sb3JzKSArCiAgdGhlbWVfYncoKSArIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgxLjEpKSkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CmJwIDwtIGdncGxvdChnYXBtaW5kZXIsCiAgICAgICBhZXMoeCA9IHllYXIsIHkgPSBsaWZlRXhwLCBncm91cCA9IGNvdW50cnksIGNvbG9yID0gY291bnRyeSkKICAgICAgICkgKwogIGdlb21fbGluZShsd2QgPSAxLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIGZhY2V0X3dyYXAofiBjb250aW5lbnQpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY291bnRyeV9jb2xvcnMpICsgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDEuMSkpKQpicApgYGAKCmBgYHtyIGV2YWw9VFJVRSxtZXNzYWdlPUZBTFNFfQpwbG90bHk6OmdncGxvdGx5KGJwKQpgYGAKCgojIyMgQm94cGxvdHMKCgpgYGB7ciBldmFsPVRSVUV9CiMgbm93IGl0IGlzIGEgY2F0ZWdvcmljYWwgeCBWUyBjb250aW51b3VzIHkKcCA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGNvbnRpbmVudCwgeSA9IGxpZmVFeHApKSAKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KCkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KGFscGhhPTEvNCkKYGBgCgpJdCBpcyBzbyBlYXN5IHRvIGVzY2FwZSAqZHluYW1pdGUqIHBsb3RzIQoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9qaXR0ZXIoKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21faml0dGVyKGFlcyhjb2w9Y29udGluZW50KSkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fYm94cGxvdCgpICsgZ2VvbV9qaXR0ZXIoYWxwaGE9MS8yKQpgYGAKCiMjIyBIaXN0b2dyYW1zCgpgYGB7ciBldmFsPVRSVUV9CnAgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKGxpZmVFeHApKQpwICsgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTEpCmBgYAoKU3RhY2tlZCBoaXN0b2dyYW0gYXJlIG11Y2ggZWFzaWVyIGluIHRoaXMgZnJhbWV3b3JrCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX2hpc3RvZ3JhbShhZXMoY29sb3I9Y29udGluZW50KSkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbD1jb250aW5lbnQpKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsPWNvbnRpbmVudCksIHBvc2l0aW9uPSJpZGVudGl0eSIpCmBgYAoKLi4uIGFuZCBzbyBpcyB0aGUgc3VwZXJpbXBvc2luZyBvZiBtb3JlIHRoYW4gb25lIGRpc3RyaWJ1dGlvbgoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGw9Y29udGluZW50KSwgcG9zaXRpb249ImlkZW50aXR5IiwgYWxwaGEgPSAwLjQpCmBgYAoKU2ltaWxhciB0byBoaXN0b2dyYW0sIHlvdSBjYW4gdXNlIGFsc28gZGVuc2l0eSBwbG90cwoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9kZW5zaXR5KGFlcyhmaWxsPWNvbnRpbmVudCksIGFscGhhPTEvNCkKYGBgCgojIyMgVGhlbWVzOiBhIHF1aWNrIHdheSB0byBwdXQgYSBuZXcgc2hpcnQgb24KCmBgYHtyIGV2YWw9VFJVRX0KbmljZW9uZSArIHRoZW1lX2J3KCkKYGBgCgpgYGB7ciBldmFsPVRSVUV9Cm5pY2VvbmUgKyB0aGVtZV92b2lkKCkKYGBgCgpJZiB5b3UgcmVhbGx5IHJlYWxseSByZWFsbHkgaGF2ZSB0by4uLgoKYGBge3IgZXZhbD1UUlVFfQpsaWJyYXJ5KCJnZ3RoZW1lcyIpCm5pY2VvbmUgKyB0aGVtZV9leGNlbCgpICsgc2NhbGVfY29sb3JfZXhjZWwoKQpgYGAKCiMjIEV4ZXJjaXNlIHNlc3Npb24gNiAtIEhvbWV3b3JrIGlmIHlvdSB3YW50CgotIHRyeSB0byByZWNyZWF0ZSB0aGUgcGxvdHMgeW91IGRpZCB3aXRoIGJhc2UgZ3JhcGhpY3MsIHRoaXMgdGltZSB1c2luZyBgZ2dwbG90MmAgCgotIHBpY2sgYSBuaWNlIHBsb3QgeW91IHdvdWxkIGxpa2UgdG8gaGF2ZSBpbiB5b3VyIG5leHQgbWFudXNjcmlwdDogY2FuIHlvdSB0aGluayBvZiB3aGF0IHlvdSBuZWVkIHRvIGRvIGl0PyBJIGFtIHRhbGtpbmcgb2YgCiAgICAtIHdoYXQgZGF0YSB0eXBlPwogICAgLSB3aGF0IHRyYW5zZm9ybWF0aW9ucz8KICAgIC0gd2hhdCBwbG90IHR5cGUvbGF5ZXI/CgoKPCEtLSAjIHRhYnVsYXIgZGF0YSBhbmFseXNpcyAtIG9uIHRoZSBSTkEgZGF0YXNldD8gLS0+Cgo8IS0tICMgZGF0YSB2aXN1YWxpemF0aW9uIC0tPgoKPCEtLSAjIGpvaW5pbmcgdGFibGVzICg/KSAtLT4KCjxkZXRhaWxzPgo8L2RldGFpbHM+CgojIFN0ZXAgNTogU3VtbWFyaXplZEV4cGVyaW1lbnQ6IHlvdXIgYmVzdCBmcmllbmQgZm9yICJiaW9pbmZvcm1hdGljcyBkYXRhc2V0cyIKCgojIyBOZXh0IHN0ZXBzCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoInRpZHl2ZXJzZSIpCmBgYAoKRGF0YSBpbiBiaW9pbmZvcm1hdGljcyBpcyBvZnRlbiBjb21wbGV4LgpUbyBkZWFsIHdpdGggdGhpcywgZGV2ZWxvcGVycyBkZWZpbmUgc3BlY2lhbGlzZWQgZGF0YSBjb250YWluZXJzICh0ZXJtZWQgY2xhc3NlcykgdGhhdCBtYXRjaCB0aGUgcHJvcGVydGllcyBvZiB0aGUgZGF0YSB0aGV5IG5lZWQgdG8gaGFuZGxlLgoKVGhpcyBhc3BlY3QgaXMgY2VudHJhbCB0byB0aGUgKipCaW9jb25kdWN0b3IqKihodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnKSBwcm9qZWN0IHdoaWNoIHVzZXMgdGhlIHNhbWUgKipjb3JlIGRhdGEgaW5mcmFzdHJ1Y3R1cmUqKiBhY3Jvc3MgcGFja2FnZXMuIFRoaXMgY2VydGFpbmx5IGNvbnRyaWJ1dGVkIHRvIEJpb2NvbmR1Y3RvcidzIHN1Y2Nlc3MuIEJpb2NvbmR1Y3RvciBwYWNrYWdlIGRldmVsb3BlcnMgYXJlIGFkdmlzZWQgdG8gbWFrZSB1c2Ugb2YgZXhpc3RpbmcgaW5mcmFzdHJ1Y3R1cmUgdG8gcHJvdmlkZSBjb2hlcmVuY2UsIGludGVyb3BlcmFiaWxpdHkgYW5kIHN0YWJpbGl0eSB0byB0aGUgcHJvamVjdCBhcyBhIHdob2xlLgoKClRvIGlsbHVzdHJhdGUgc3VjaCBhbiBvbWljcyBkYXRhIGNvbnRhaW5lciwgd2UnbGwgcHJlc2VudCB0aGUgYFN1bW1hcml6ZWRFeHBlcmltZW50YCBjbGFzcy4KCiMjIFN1bW1hcml6ZWRFeHBlcmltZW50CgpUaGUgZmlndXJlIGJlbG93IHJlcHJlc2VudHMgdGhlIGFuYXRvbXkgb2YgU3VtbWFyaXplZEV4cGVyaW1lbnQuCgpgYGB7ciBTRSwgIGVjaG89RkFMU0UsIG91dC53aWR0aCA9ICc4MCUnfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzL1NFLnN2ZyIpCmBgYAoKYGBge3IgbG9hZGRhdGFfZHBseXIsIGVjaG89RkFMU0UsIHB1cmw9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgZGlyLmNyZWF0ZSgiZGF0YSIpCmlmICghZmlsZS5leGlzdHMoImRhdGEvcm5hc2VxLmNzdiIpKQpkb3dubG9hZC5maWxlKHVybCA9ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQmlvY29uZHVjdG9yL2Jpb2NvbmR1Y3Rvci10ZWFjaGluZy9tYXN0ZXIvZGF0YS9HU0U5Njg3MC9ybmFzZXEuY3N2IiwKICAgICAgICAgICAgICBkZXN0ZmlsZSA9ICJkYXRhL3JuYXNlcS5jc3YiKQpgYGAKCgpPYmplY3RzIG9mIHRoZSBjbGFzcyBTdW1tYXJpemVkRXhwZXJpbWVudCBjb250YWluIDoKCi0gKipPbmUgKG9yIG1vcmUpIGFzc2F5KHMpKiogY29udGFpbmluZyB0aGUgcXVhbnRpdGF0aXZlIG9taWNzIGRhdGEgKGV4cHJlc3Npb24gZGF0YSksIHN0b3JlZCBhcyBhIG1hdHJpeC1saWtlIG9iamVjdC4gRmVhdHVyZXMgKGdlbmVzLCB0cmFuc2NyaXB0cywgcHJvdGVpbnMsIC4uLikgYXJlIGRlZmluZWQgYWxvbmcgdGhlIHJvd3MgYW5kIHNhbXBsZXMgYWxvbmcgdGhlIGNvbHVtbnMuCgotIEEgKipzYW1wbGUgbWV0YWRhdGEqKiBzbG90IGNvbnRhaW5pbmcgc2FtcGxlIGNvLXZhcmlhdGVzLCBzdG9yZWQgYXMgYSBkYXRhIGZyYW1lLiBSb3dzIGZyb20gdGhpcyB0YWJsZSByZXByZXNlbnQgc2FtcGxlcyAocm93cyBtYXRjaCBleGFjdGx5IHRoZSBjb2x1bW5zIG9mIHRoZSBleHByZXNzaW9uIGRhdGEpLgoKLSBBICoqZmVhdHVyZSBtZXRhZGF0YSoqIHNsb3QgY29udGFpbmluZyBmZWF0dXJlIGNvLXZhcmlhdGVzLCBzdG9yZWQgYXMgZGF0YSBmcmFtZS4gVGhlIHJvd3Mgb2YgdGhpcyBkYXRhZnJhbWUncyBtYXRjaCBleGFjdGx5IHRoZSByb3dzIG9mIHRoZSBleHByZXNzaW9uIGRhdGEuCgpUaGUgY29vcmRpbmF0ZWQgbmF0dXJlIG9mIHRoZSBTdW1tYXJpemVkRXhwZXJpbWVudCBndWFyYW50ZWVzIHRoYXQgZHVyaW5nIGRhdGEgbWFuaXB1bGF0aW9uLCB0aGUgZGltZW5zaW9ucyBvZiB0aGUgZGlmZmVyZW50IHNsb3RzIHdpbGwgYWx3YXlzIG1hdGNoIChpLmUgdGhlIGNvbHVtbnMgaW4gdGhlIGV4cHJlc3Npb24gZGF0YSBhbmQgdGhlbiByb3dzIGluIHRoZSBzYW1wbGUgbWV0YWRhdGEsIGFzIHdlbGwgYXMgdGhlIHJvd3MgaW4gdGhlIGV4cHJlc3Npb24gZGF0YSBhbmQgZmVhdHVyZSBtZXRhZGF0YSkgZHVyaW5nIGRhdGEgbWFuaXB1bGF0aW9uLiBGb3IgZXhhbXBsZSwgaWYgd2UgaGFkIHRvIGV4Y2x1ZGUgb25lIHNhbXBsZSBmcm9tIHRoZSBhc3NheSwgaXQgd291bGQgYmUgYXV0b21hdGljYWxseSByZW1vdmVkIGZyb20gdGhlIHNhbXBsZSBtZXRhZGF0YSBpbiB0aGUgc2FtZSBvcGVyYXRpb24uCgpUaGUgbWV0YWRhdGEgc2xvdHMgY2FuIGdyb3cgYWRkaXRpb25hbCBjby12YXJpYXRlcyAoY29sdW1ucykgd2l0aG91dCBhZmZlY3RpbmcgdGhlIG90aGVyIHN0cnVjdHVyZXMuCgoqKlF1ZXN0aW9ucyoqICAKUTEgLSBDYW4geW91IHRoaW5rIG9mIGRhdGEgZXhhbXBsZXMgd2hhdCBjYW4gZml0IGludG8gdGhpcyBjb250YWluZXI/ICAKUTIgLSBXaGF0IGlmIHRoZSBkYXRhIGhhcyBzb21lICJzcGVjaWZpYyIgcGVjdWxpYXJpdGllcyBvbiB0b3Agb2YgdGhpcyB0YWJ1bGFyLWxpa2UgcmVwcmVzZW50YXRpb24/CgoKIyMjIENyZWF0aW5nIGEgU3VtbWFyaXplZEV4cGVyaW1lbnQKCmBgYHtyfQpybmEgPC0gcmVhZF9jc3YoImRhdGEvcm5hc2VxLmNzdiIpCmhlYWQocm5hKQpoZWFkKGFzLmRhdGEuZnJhbWUocm5hKSkKYGBgCgoKYGBge3IgLCBlY2hvID0gRiwgbWVzc2FnZSA9IEZBTFNFLCBldmFsID0gRkFMU0V9CnJuYSA8LSByZWFkX2NzdigiZGF0YS9ybmFzZXEuY3N2IikKY291bnRzIDwtIHJuYSAlPiUKICBzZWxlY3QoZ2VuZSwgc2FtcGxlLCBleHByZXNzaW9uKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc2FtcGxlLAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gZXhwcmVzc2lvbikKY291bnRfbWF0cml4IDwtIGNvdW50cyAlPiUgc2VsZWN0KC1nZW5lKSAlPiUgYXMubWF0cml4KCkKcm93bmFtZXMoY291bnRfbWF0cml4KSA8LSBjb3VudHMkZ2VuZQpzYW1wbGVfbWV0YWRhdGEgPC0gcm5hICU+JQogIHNlbGVjdChzYW1wbGUsIG9yZ2FuaXNtLCBhZ2UsIHNleCwgaW5mZWN0aW9uLCBzdHJhaW4sIHRpbWUsIHRpc3N1ZSwgbW91c2UpCnNhbXBsZV9tZXRhZGF0YSA8LSB1bmlxdWUoc2FtcGxlX21ldGFkYXRhKQpnZW5lX21ldGFkYXRhIDwtIHJuYSAlPiUKICBzZWxlY3QoZ2VuZSwgRU5UUkVaSUQsIHByb2R1Y3QsIGVuc2VtYmxfZ2VuZV9pZCwgZXh0ZXJuYWxfc3lub255bSwgY2hyb21vc29tZV9uYW1lLCBnZW5lX2Jpb3R5cGUsIHBoZW5vdHlwZV9kZXNjcmlwdGlvbiwgaHNhcGllbnNfaG9tb2xvZ19hc3NvY2lhdGVkX2dlbmVfbmFtZSkKIyBSZW1vdmUgcmVkdW5kYW5jeQpnZW5lX21ldGFkYXRhIDwtIHVuaXF1ZShnZW5lX21ldGFkYXRhKQoKCnNhdmVSRFMoY291bnRfbWF0cml4LCAiZGF0YS9jb3VudF9tYXRyaXguUkRTIikKc2F2ZVJEUyhzYW1wbGVfbWV0YWRhdGEsICJkYXRhL3NhbXBsZV9tZXRhZGF0YS5SRFMiKQpzYXZlUkRTKGdlbmVfbWV0YWRhdGEsICJkYXRhL2dlbmVfbWV0YWRhdGEuUkRTIikKYGBgCgoKClJlbWVtYmVyIHRoZSBgcm5hYCBkYXRhc2V0IHRoYXQgd2UgaGF2ZSB1c2VkIHByZXZpb3VzbHkuCgpGcm9tIHRoaXMgdGFibGUgd2UgaGF2ZSBhbHJlYWR5IGNyZWF0ZWQgMyBkaWZmZXJlbnQgdGFibGVzIC0gd2UgcmVhZCB0aGVtIGluIGFzIHNlcmlhbGl6ZWQgciBvYmplY3RzLgoKYGBge3J9CmNvdW50X21hdHJpeCA8LSByZWFkUkRTKCJkYXRhL2NvdW50X21hdHJpeC5SRFMiKQpzYW1wbGVfbWV0YWRhdGEgPC0gcmVhZFJEUygiZGF0YS9zYW1wbGVfbWV0YWRhdGEuUkRTIikKZ2VuZV9tZXRhZGF0YSA8LSByZWFkUkRTKCJkYXRhL2dlbmVfbWV0YWRhdGEuUkRTIikKYGBgCgoKLSAqKkFuIGV4cHJlc3Npb24gbWF0cml4KioKCmBgYHtyfQpjb3VudF9tYXRyaXhbMTo1LCBdCmRpbShjb3VudF9tYXRyaXgpCmBgYAoKCi0gKipBIHRhYmxlIGRlc2NyaWJpbmcgdGhlIHNhbXBsZXMqKgoKYGBge3J9CnNhbXBsZV9tZXRhZGF0YQpgYGAKCi0gKipBIHRhYmxlIGRlc2NyaWJpbmcgdGhlIGdlbmVzKioKCmBgYHtyfQpnZW5lX21ldGFkYXRhCmBgYAoKV2Ugd2lsbCBjcmVhdGUgYSBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIGZyb20gdGhlc2UgdGFibGVzOgoKLSBUaGUgY291bnQgbWF0cml4IHRoYXQgd2lsbCBiZSB1c2VkIGFzIHRoZSAqKmBhc3NheWAqKgoKLSBUaGUgdGFibGUgZGVzY3JpYmluZyB0aGUgc2FtcGxlcyB3aWxsIGJlIHVzZWQgYXMgdGhlICoqc2FtcGxlIG1ldGFkYXRhKiogc2xvdAoKLSBUaGUgdGFibGUgZGVzY3JpYmluZyB0aGUgZ2VuZXMgd2lsbCBiZSB1c2VkIGFzIHRoZSAqKmZlYXR1cmVzIG1ldGFkYXRhKiogc2xvdAoKVG8gZG8gdGhpcyB3ZSBjYW4gcHV0IHRoZSBkaWZmZXJlbnQgcGFydHMgdG9nZXRoZXIgdXNpbmcgdGhlCmBTdW1tYXJpemVkRXhwZXJpbWVudGAgY29uc3RydWN0b3I6CgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQojQmlvY01hbmFnZXI6Omluc3RhbGwoIlN1bW1hcml6ZWRFeHBlcmltZW50IikKbGlicmFyeSgiU3VtbWFyaXplZEV4cGVyaW1lbnQiKQpgYGAKCmBgYHtyfQpzZSA8LSBTdW1tYXJpemVkRXhwZXJpbWVudChhc3NheXMgPSBjb3VudF9tYXRyaXgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBzYW1wbGVfbWV0YWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd0RhdGEgPSBnZW5lX21ldGFkYXRhKQpzZQpgYGAKCmBgYHtyLCBlY2hvID0gRkFMU0V9CnNhdmUoc2UsIGZpbGUgPSAiZGF0YS9TRS5yZGEiKQpgYGAKClVzaW5nIHRoaXMgZGF0YSBzdHJ1Y3R1cmUsIHdlIGNhbiBhY2Nlc3MgdGhlIGV4cHJlc3Npb24gbWF0cml4IHdpdGgKdGhlIGBhc3NheWAgZnVuY3Rpb246CgoKYGBge3J9CmhlYWQoYXNzYXkoc2UpKQpkaW0oYXNzYXkoc2UpKQpgYGAKCldlIGNhbiBhY2Nlc3MgdGhlIHNhbXBsZSBtZXRhZGF0YSB1c2luZyB0aGUgYGNvbERhdGFgIGZ1bmN0aW9uOgoKYGBge3J9CmNvbERhdGEoc2UpCmRpbShjb2xEYXRhKHNlKSkKYGBgCgpXZSBjYW4gYWxzbyBhY2Nlc3MgdGhlIGZlYXR1cmUgbWV0YWRhdGEgdXNpbmcgdGhlIGByb3dEYXRhYCBmdW5jdGlvbjoKCmBgYHtyfQpoZWFkKHJvd0RhdGEoc2UpKQpkaW0ocm93RGF0YShzZSkpCmBgYAoKCiMjIyBTdWJzZXR0aW5nIGEgU3VtbWFyaXplZEV4cGVyaW1lbnQKClN1bW1hcml6ZWRFeHBlcmltZW50IGNhbiBiZSBzdWJzZXQganVzdCBsaWtlIHdpdGggZGF0YSBmcmFtZXMsCndpdGggbnVtZXJpY3Mgb3Igd2l0aCBjaGFyYWN0ZXJzIG9mIGxvZ2ljYWxzLgoKQmVsb3csIHdlIGNyZWF0ZSBhIG5ldyBpbnN0YW5jZSBvZiBjbGFzcyBTdW1tYXJpemVkRXhwZXJpbWVudCB0aGF0IGNvbnRhaW5zIG9ubHkKdGhlIDUgZmlyc3QgZmVhdHVyZXMgZm9yIHRoZSAzIGZpcnN0IHNhbXBsZXMuCgpgYGB7cn0Kc2UxIDwtIHNlWzE6NSwgMTozXQpzZTEKYGBgCgpgYGB7cn0KY29sRGF0YShzZTEpCnJvd0RhdGEoc2UxKQpgYGAKCgpXZSBjYW4gYWxzbyB1c2UgdGhlIGBjb2xEYXRhKClgIGZ1bmN0aW9uIHRvIHN1YnNldCBvbiBzb21ldGhpbmcgZnJvbSB0aGUgc2FtcGxlIG1ldGFkYXRhLCBvciB0aGUgYHJvd0RhdGEoKWAgdG8gc3Vic2V0IG9uIHNvbWV0aGluZyBmcm9tIHRoZSBmZWF0dXJlIG1ldGFkYXRhLgpGb3IgZXhhbXBsZSwgaGVyZSB3ZSBrZWVwIG9ubHkgbWlSTkFzIGFuZCB0aGUgbm9uIGluZmVjdGVkIHNhbXBsZXM6CgpgYGB7cn0Kc2UxIDwtIHNlW3Jvd0RhdGEoc2UpJGdlbmVfYmlvdHlwZSA9PSAibWlSTkEiLAogICAgICAgICAgY29sRGF0YShzZSkkaW5mZWN0aW9uID09ICJOb25JbmZlY3RlZCJdCnNlMQphc3NheShzZTEpCmNvbERhdGEoc2UxKQpyb3dEYXRhKHNlMSkKYGBgCgoKCgpGb3IgdGhlIGZvbGxvd2luZyBleGVyY2lzZSwgeW91IHNob3VsZCBkb3dubG9hZCB0aGUgU0UucmRhIG9iamVjdAoodGhhdCBjb250YWlucyB0aGUgYHNlYCBvYmplY3QpLCBhbmQgb3BlbiB0aGUgZmlsZSB1c2luZyB0aGUKJ2xvYWQoKScgZnVuY3Rpb24uCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpkb3dubG9hZC5maWxlKHVybCA9ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vVUNMb3V2YWluLUNCSU8vYmlvaW5mby10cmFpbmluZy0wMS1pbnRyby1yL21hc3Rlci9kYXRhL1NFLnJkYSIsCiAgICAgICAgICAgICAgZGVzdGZpbGUgPSAiZGF0YS9TRS5yZGEiKQpsb2FkKGZpbGUgPSAiZGF0YS9TRS5yZGEiKQpgYGAKCiMjIEV4ZXJjaXNlIHNlc3Npb24gNgoKRXh0cmFjdCB0aGUgZ2VuZSBleHByZXNzaW9uIGxldmVscyBvZiB0aGUgMyBmaXJzdCBnZW5lcyBpbiBzYW1wbGVzIGF0IHRpbWUgMCBhbmQgYXQgdGltZSA4LgoKIyMjIEV4ZXJjaXNlIHNlc3Npb24gNiAtIFNvbHV0aW9ucwoKPGRldGFpbHM+CgpgYGB7cn0KYXNzYXkoc2UpWzE6MywgY29sRGF0YShzZSkkdGltZSAhPSA0XQoKIyBFcXVpdmFsZW50IHRvCmFzc2F5KHNlKVsxOjMsIGNvbERhdGEoc2UpJHRpbWUgPT0gMCB8IGNvbERhdGEoc2UpJHRpbWUgPT0gOF0KYGBgCgo8L2RldGFpbHM+CgojIyMjIEFkZGluZyB2YXJpYWJsZXMgdG8gbWV0YWRhdGEKCldlIGNhbiBhbHNvIGFkZCBpbmZvcm1hdGlvbiB0byB0aGUgbWV0YWRhdGEuClN1cHBvc2UgdGhhdCB5b3Ugd2FudCB0byBhZGQgdGhlIGNlbnRlciB3aGVyZSB0aGUgc2FtcGxlcyB3ZXJlIGNvbGxlY3RlZC4uLgoKYGBge3J9CmNvbERhdGEoc2UpJGNlbnRlciA8LSByZXAoIlVuaXZlcnNpdHkgb2YgSWxsaW5vaXMiLCBucm93KGNvbERhdGEoc2UpKSkKY29sRGF0YShzZSkKYGBgCgpUaGlzIGlsbHVzdHJhdGVzIHRoYXQgdGhlIG1ldGFkYXRhIHNsb3RzIGNhbiBncm93IGluZGVmaW5pdGVseSB3aXRob3V0IGFmZmVjdGluZwp0aGUgb3RoZXIgc3RydWN0dXJlcyEKCgoKKipUYWtlLWhvbWUgbWVzc2FnZSoqCgoKLSBgU3VtbWFyaXplZEV4cGVyaW1lbnRgIHJlcHJlc2VudCBhbiBlZmZpY2llbnQgd2F5IHRvIHN0b3JlIGFuZCB0byBoYW5kbGUgb21pY3MgZGF0YS4KCi0gVGhleSBhcmUgdXNlZCBpbiBtYW55IEJpb2NvbmR1Y3RvciBwYWNrYWdlcy4KCklmIHlvdSBmb2xsb3cgbmV4dCB0cmFpbmluZyBmb2N1c2VkIG9uIFJOQSBzZXF1ZW5jaW5nIGFuYWx5c2lzLCB5b3Ugd2lsbCBsZWFybiB0bwp1c2UgdGhlIEJpb2NvbmR1Y3RvciBgREVTZXEyYCBwYWNrYWdlIHRvIGRvIHNvbWUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzZXMuCmBERVNlcTJgJ3Mgd2hvbGUgYW5hbHlzaXMgaXMgaGFuZGxlZCBpbiBhIGBTdW1tYXJpemVkRXhwZXJpbWVudGAuCgoKIyBVc2VmdWwgbWF0ZXJpYWwKCkJvb2tzCgotIFIgaW4gYSBudXRzaGVsbCwgUiBjb29rYm9vaywgUiBncmFwaGljcyBjb29rYm9vayAoQE8nUmVpbGx5IG1lZGlhKQotIEEgQmVnaW5uZXIncyBHdWlkZSB0byBSIChadXVyLCBJZW5vLCBNZWVzdGVycywgQFNwcmluZ2VyKQotIFIgUHJvZ3JhbW1pbmcgZm9yIERhdGEgU2NpZW5jZSAoUGVuZywgQExlYW5wdWIpCi0gUiBQcm9ncmFtbWluZyBmb3IgQmlvaW5mb3JtYXRpY3MgKEdlbnRsZW1hbiwgQENSQykKLSBCaW9jb25kdWN0b3IgQ2FzZSBTdHVkaWVzIChAU3ByaW5nZXIpCi0gRGF0YSBBbmFseXNpcyBmb3IgdGhlIExpZmUgU2NpZW5jZXMgKElyaXphcnJ5LCBMb3ZlLCBATGVhbnB1YikKLSBCaW9jb25kdWN0b3IgLSBBbiBJbnRyb2R1Y3Rpb24gdG8gQ29yZSBUZWNobm9sb2dpZXMgKEhhbnNlbiwgQExlYW5wdWIpCi0gUiBmb3IgRGF0YSBTY2llbmNlIChXaWNraGFtLCBHcm9sZW11bmQsIEBPJ1JlaWxseSkKCi0gdGhlIHdob2xlIGBVc2UgUiFgIGJvb2sgc2VyaWVzOiBodHRwczovL2xpbmsuc3ByaW5nZXIuY29tL2Jvb2tzZXJpZXMvNjk5MQotIHRoaXMgb25lIGZyb20gQ1JDOiBodHRwczovL3d3dy5jcmNwcmVzcy5jb20vQ2hhcG1hbi0tSGFsbENSQy1UaGUtUi1TZXJpZXMvYm9vay1zZXJpZXMvQ1JDVEhFUlNFUgotIGh0dHBzOi8vbGluay5zcHJpbmdlci5jb20vYm9vay8xMC4xMDA3Lzk3OC0zLTY2Mi01MzY3MC00Ci0gaHR0cHM6Ly9saW5rLnNwcmluZ2VyLmNvbS9ib29rLzEwLjEwMDcvOTc4LTMtNjYyLTQ5MTAyLTcKCgpDb3Vyc2VzCgotIGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb3Vyc2VzL2ZyZWUtaW50cm9kdWN0aW9uLXRvLXIKLSBodHRwczovL3d3dy5jb3Vyc2VyYS5vcmcvc3BlY2lhbGl6YXRpb25zL2podS1kYXRhLXNjaWVuY2UKLSBodHRwczovL3d3dy5lZHgub3JnL2NvdXJzZS9pbnRyb2R1Y3Rpb24tci1kYXRhLXNjaWVuY2UtbWljcm9zb2Z0LWRhdDIwNHgtNwoKTWlzYwoKLSBodHRwOi8vcjRzdGF0cy5jb20vYXJ0aWNsZXMvd2h5LXItaXMtaGFyZC10by1sZWFybi8KLSBodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvCi0gc3dpcmwgLSBsZWFybiBSIGluIFI6IGh0dHA6Ly9zd2lybHN0YXRzLmNvbS8KCjwhLS0gIyMgV2hhdCB3ZSBsZWZ0IG91dCAtLT4KCjwhLS0gLSBhZHZhbmNlZCBkYXRhIG1hbmlwdWxhdGlvbiAtIGBkcGx5cmAgYW5kIHRoZSBtaWdodHkgcGlwZSAtLT4KPCEtLSAtIHN0cmluZyBtYW5pcHVsYXRpb25zIC0tPgo8IS0tIC0gZ2dwbG90MiBpbiBtb3JlIGRldGFpbCAtLT4KPCEtLSAtIHBhY2thZ2UgZGV2ZWxvcG1lbnQgKHlvdXIgb3duIHBhY2thZ2UpIC0tPgo8IS0tIC0gc2hpbnkgYXBwcyBkZXZlbG9wbWVudCAoZnJvbSAwIHRvIGFwcCkgLS0+CjwhLS0gLSByZXByb2R1Y2libGUgcmVwb3J0cyAoYWxzbyBpbnRlcmFjdGl2ZSEpIC0tPgoKCgojIFNlc3Npb24gSW5mbyB7LnVubnVtYmVyZWR9CgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAK